使用 ValueObjects 不是实体。
在注册的情况下,可以引入一个 UserName 值对象。接收注册时创建一个用户名对象。在 UserName 的构造函数中实现验证。
查看question 和presentation 了解更多详情。
编辑1:
1.如何处理不同验证规则应用于不同上下文的情况。
例如:某类会员的用户名不能有数字,而其他类型的会员则需要?
也许不同的工厂方法可以做到这一点。像 UserName.forGoldenCardMember(...) 或 UserName.forPlainMember(...)。或者制作 MemberType (可能是一个层次结构)来验证 UserName。
另一种替代方案是使用 AggregateFactory(在本例中为 AccountFactory)。
2.构造函数是唯一放置验证码的地方吗?我确实在网上阅读了两种观点:对象必须始终有效与不总是有效。两者都提出了很好的论点,但还有其他方法吗?
我个人更喜欢有效的方法。传递一个可能无效的值对象会损害封装性。
编辑2:
要求
a) 基于上下文的验证业务规则(成员类型不同的用户名规则)
b) 继续验证所有业务规则,即使其中一个失败
坚持单一职责原则,使用值对象(本例为成员类型)。
可以引入 AggregateFactory 来简化应用层(更粗粒度)。
class AccoutFactory {
Account registerWith(Username username, MemberType type, ....) {
List<String> errors = new ArrayList<String>();
errors.addAll(type.listErrorsWith(username));
errors.add(//other error report...
if (CollectionUtils.isEmpty(errors)) {
return new Account(username,....);
} else {
throw new CannotRegisterAccountException(errors);
}
}
}
编辑3:
对于 cmets 中的问题
a)用户名对象不应该是具有返回错误的方法的对象吗
listErrorsWith()?毕竟是用户名对不同的会员类型有不同的规则?
我们可以从另一个角度来检查这个问题:MemberTypes 对用户名有不同的规则。这可以用多态性替换 Username.listErrosWith(String, MemeberType) 中的 if/else 块;
b) 如果我们在 MemberType 中有方法,则知识不会被封装在 Username 中。另外,我们正在讨论确保 Username 始终有效。
我们可以在没有 MemberType 规则的情况下定义 Username 的有效性。假设“hippoom@stackoverflow.com”是一个有效的用户名,它适合 GoldenCard 会员,但不适合 SilverCard 会员。
c) 我仍然看不到如何执行返回错误列表的验证,而无需从构造函数或静态方法抛出的异常中获取列表。恕我直言,两者看起来都不理想。
是的,listErrorsWith():List 的签名看起来很奇怪,我宁愿使用没有返回值的 validate(username) (失败时抛出异常)。但这将迫使客户端捕获每个验证步骤以一次运行验证。