假设您在Value对象和Services对象中划分系统(如“面向对象的软件增长,测试指导”中所述.Misko Hevery称这些为“newables”和“injectables”。
如果您的某个值对象突然需要访问服务来实现它的方法,会发生什么?
假设你有一个很好的简单Value对象。它是不可变的,包含一些信息,而且就是它。假设我们使用类似这样的东西:
CreditCard card = new CreditCard("4111-1111-1111-1111", "07/10");
if (card.isValid())
{
// do stuff
}
else
{
// don't do stuff
}
到目前为止一切顺利。 isValid()
在卡号上实现校验位算法并返回true / false。
现在,假设我希望通过验证当前时间的到期日来增强系统。您如何在不破坏Value对象/服务对象paradim的情况下完成此操作?我希望这个课程能够继续进行单元测试。
CreditCard
现在有一个依赖项,但由于它的创建方式,它无法注入,因此依赖注入已经完成。CreditCard
课程不应该呼唤单身人士(我认为全球访问单身人士是不好的做法)CreditCardVerificationService.validateCard()
上意味着必须重新访问所有现有代码。 isValid()的实现正在泄漏。我知道可以采取一些措施来解决这个问题,但最简洁的方法是什么?
答案 0 :(得分:4)
我认为验证任何东西都不是CreditCard对象的工作。工厂将验证校验位以确保它实例化符合要求的卡,而验证服务将验证卡是否到期/ $ limit。
答案 1 :(得分:1)
我很想说CreditCard
不是价值对象。
来自C2 wiki:
价值对象的例子就是事物 喜欢数字,日期,钱和 字符串。通常,它们很小 广泛使用的对象。 他们的身份是基于他们的国家 而不是他们的对象身份。 这样,您就可以拥有多个副本 相同的概念价值对象。
值对象不是 BusinessObject的/ ReferenceObject。一个 BusinessObject / ReferenceObject是 你在世界上找到的东西,而 ValueObject是一个度量或 对某事的描述。
如果CreditCardNumber
可能是值对象,CreditCard
看起来更像是包含某些业务逻辑的业务对象,例如验证
我通常拥有价值对象,服务和业务对象。我不知道“增长的面向对象的软件”,但限制自己仅限于价值对象和服务对我来说似乎很奇怪。
答案 2 :(得分:0)
我会将CreditCard
称为实体而不是值对象,因为它可能是持久的并且具有唯一的身份。
无论如何,实体类使用服务类应该是完全正确的。如果不需要在运行时基于外部配置选择所述服务的实现,那么我将简单地在客户端方法中实例化并使用所需的服务类。与某些人的想法相反,这并不排除单元测试,因为可以使用模拟工具进行隔离。
如果需要在运行时选择服务实现 ,则可以使用服务定位器。这种模式可以为模拟/伪造提供直接支持,而无需专门的模拟工具。使用支持注入“新”对象的DI框架将是另一种选择。