6

正如您在下面看到的,在构造函数中,我正在实例化一个验证对象,因此我可以在 set 方法中验证用户的电子邮件。这种架构是最佳实践还是有缺陷?我可以避免让我的 User 类直接依赖于我的 Validation 类吗?

Class User {
  Private Email

//constructor
User() {
  Validation = new Validation
}

SetEmail(NewValue) {
  if (Validation.isEmail(NewValue)) {
    Email = NewValue
  }
}

还有一个相关的问题:当 set 方法收到无效值时,正确的响应是什么?我看到 2 个选项

  1. 不要设置值并返回 false
  2. 无论如何设置该值,但为该对象设置一个错误属性。(所以如果设置了 User.Error 我知道出了点问题)

我怀疑#1 是最佳实践,因为您可以确保任何对象属性的值始终有效。正确的?

4

2 回答 2

10

到目前为止,这些提议似乎都太过分了,尤其是对于所有 IOC 和 AOP 的东西。

  1. 该类User需要一个电子邮件地址,因此创建一个EmailAddress类并让User该类通过属性和/或其构造函数接受一个。该验证可以像输入EmailAddress引用是否为空一样简单。

  2. 该类EmailAddress可以是一个简单但通常可重用的实现(考虑基于 RFC 文档)。它应该是不可变的,并且应该在输入无效时从其构造函数中抛出异常。

  3. 理想情况下,EmailAddress类应该由一个EmailUserId类(基于 RFC?)和一个InternetDomain类(基于 RFC?)组成,因为电子邮件地址是一个复合数据结构。同样,这些类中的每一个都应该管理不可变的实例,并且应该在输入无效的构造时抛出异常。

“验证”给我的印象不是“事物”,而是通用的“动作”。因此,它适合作为一个方法而不是一个类。在这种情况下,我倾向于将每个类中的验证实现为valid(input)从构造函数调用的私有静态方法 (),使用 Java 或 C# 等语言。isValid(input)通常,以问题 ( )的形式公开该功能会变得很有用。

编辑:

您是否建议我需要验证的每种不同的数据类型都应该有自己的类?

这是解决问题的一种可靠方法,通常称为值类型(感谢弗兰克的提醒)。结果将是几个(十几个或两个)定义明确、可重用的类,比如可能、、、等EmailAddress。所提出的替代方案可能会产生一个“神级”,其中混合了不可重用、不易实现的功能测试,维护困难。PhoneNumberPersonName

还有其他方法来划分解决方案,但我的建议确实具有成熟、易于理解并符合大量可靠设计原则的优势。我当然会建议在尝试发明自己的之前先尝试一下。

于 2008-12-08T05:46:26.837 回答
1

我会:

  1. 通过依赖注入打破与具体验证对象的耦合:定义一个抽象(纯虚拟)验证类,从它派生一个具体验证类,并传入(“注入”)对用户类的抽象验证类的引用构造函数。

    有关如何以及为什么在 C++ 中执行此操作的精彩讨论,请参阅 Robert Martin 1996 年关于C++ 报告中该主题的文章。

  2. 与其返回 false 或静默设置某些属性,不如引发异常。这就是他们的目的。

于 2008-12-08T03:42:44.327 回答