如下所示,在构造函数中我实例化了一个验证对象,因此我可以用set方法验证用户的电子邮件。这种架构是最佳实践还是有缺陷的?我可以避免让我的User类直接依赖于我的Validation类吗?
Class User {
Private Email
//constructor
User() {
Validation = new Validation
}
SetEmail(NewValue) {
if (Validation.isEmail(NewValue)) {
Email = NewValue
}
}
还有一个相关的问题:当set方法收到无效值时,正确的响应是什么?我看到2个选项
我怀疑#1是最佳实践,因为您可以确保任何对象属性的值始终有效。正确的吗?
答案 0 :(得分:10)
到目前为止,这些建议似乎都有点过分,特别是对于所有IOC和AOP的东西。
User
类需要一个电子邮件地址,因此请创建一个EmailAddress
类,让User
类通过属性和/或其构造函数接受一个。该验证可以像输入EmailAddress
引用是否为空一样简单。
EmailAddress
类可以是一个简单但通常可重用的实现(考虑基于RFC文档)。它应该是不可变的,并且应该在无效输入上从其构造函数中抛出异常。
理想情况下,EmailAddress
类应该由EmailUserId
类(基于RFC?)和InternetDomain
类(基于RFC?)组成,因为电子邮件地址是复合数据结构。同样,这些类中的每一个都应该管理不可变实例,并且应该在构造时抛出一个带有无效输入的异常。
“验证”对我来说不是“事物”,而是一种通用的“行动”。因此,它有助于成为一种方法而不是一种类。在这种情况下,我倾向于在每个类中实现验证,作为从构造函数调用的私有静态方法(valid(input)
),使用Java或C#等语言。通常,以问题(isValid(input)
)的形式公开该功能变得很有用。
编辑:
您是否建议我需要验证的每种不同数据类型都应该拥有自己的类?
这是解决问题的一种可靠方式,通常称为价值类型(感谢提醒,弗兰克)。结果将是一些(十二个)明确定义的,可重复使用的类,如EmailAddress
,PhoneNumber
,PersonName
等。所提出的替代方案可能会导致“上帝” “具有不可重复使用,不易测试且难以维护的功能组合”。
还有其他方法可以对解决方案进行分区,但我的建议确实具有成熟,易于理解和与大量可靠设计原则相一致的优势。我肯定会在尝试发明你自己之前尝试尝试。
答案 1 :(得分:1)
我会:
通过依赖注入打破与具体Validation对象的耦合:定义一个抽象(纯虚拟)验证类,从中派生一个具体的验证类,并传入(“注入”)对抽象的引用User类的构造函数中的验证类。
有关如何以及为何在C ++中执行此操作的优秀讨论,请参阅 The C ++ Report 中关于此主题的Robert Martin 1996 article。
不是返回false或静默设置某个属性,而是引发异常。这就是他们的目的。