当创建的类也需要运行时值时进行依赖注入?
假设您将系统划分为价值对象和服务对象(如“以测试为指导,发展面向对象的软件”中所建议的那样。Misko Hevery 称这些为“可更新对象”和“可注入对象”)。
当您的某个价值对象突然需要时会发生什么访问服务来实现它的方法?
假设您有一个很好的简单 Value 对象,它是不可变的,包含一些信息,就这样吧:
CreditCard card = new CreditCard("4111-1111-1111-1111", "07/10");
if (card.isValid())
{
// do stuff
}
else
{
// don't do stuff
}
到目前为止,一切都很好。 isValid() 在卡号上实现校验位算法并返回 true/false
现在,假设我希望通过根据当前时间验证到期日期来增强系统,您建议如何完成此操作。破坏值对象/服务对象范式?我希望此类继续可进行单元测试,
- 现在具有依赖项,但由于它的创建方式无法注入,因此依赖项。 。
- CreditCard 类不应该调用 Singleton(我认为全局访问 Singleton 是不好的做法)
- 将行为放在 CreditCardVerificationService.validateCard()< 上 /code> 意味着必须重新访问所有现有代码。 isValid() 的实现正在泄漏。
我知道可以采取一些措施来解决这个问题,但是最干净的方法是什么?
Assume you divide up your systems in Value objects and Services objects (as suggested in "Growing Object-Oriented Software, Guided by Tests". Misko Hevery calls these "newables" and "injectables".
What happens when one of your value objects suddenly needs to access a service to implement it's methods?
Let's say you have a nice simple Value object. It's immutable, holds a few bits of information and that's about it. Let's say we use it something like this:
CreditCard card = new CreditCard("4111-1111-1111-1111", "07/10");
if (card.isValid())
{
// do stuff
}
else
{
// don't do stuff
}
So far so good. isValid()
implements a check digit algorithm on the card number and returns true/false.
Now, let's say I wish to enhance the system by validating the expiry date against the current time. How would you suggest this is done without breaking the Value object/Service object paradim? I should like this class to continue to be unit testable.
CreditCard
now has a dependency, but because of the way it is created it can not be injected, so dependency injection is out.- The
CreditCard
class should not be calling out to Singletons (I am of the position that global access to a Singleton is bad practice) - Putting the behaviour on
CreditCardVerificationService.validateCard()
means all the existing code has to be revisited. The implementation of isValid() is leaking out.
I know there are things that can be done to get around this, but what is the cleanest way?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我认为验证任何东西并不是 CreditCard 对象的工作。工厂将验证校验位以确保它正在实例化合格的卡,而验证服务将验证卡的有效期/$限制。
I would argue that it isn't a CreditCard object's job to validate anything. A factory would validate the check digits to ensure that it is instantiating a conforming card, while a verification service would validate the card for expiration/$ limit.
我很想说 CreditCard 不是值对象。
来自 C2 wiki:
如果
CreditCardNumber
可以是一个值对象,那么CreditCard
看起来更像是一个包含一些业务逻辑(例如验证)的业务对象。我通常有价值对象、服务和业务对象。我不知道“发展面向对象的软件”,但将自己限制为仅值对象和服务对我来说似乎很奇怪。
I would be tempted to say that
CreditCard
is not a Value Object.From the C2 wiki:
If
CreditCardNumber
could be a value object,CreditCard
looks more like an business object which contains some business logic, e.g. validation.I usually have Value Object, Service and Business Object. I don't know about "Growing Object-Oriented Software", but restricting yourself to only Value Object and Service seems odd to me.
我会将
CreditCard
称为实体,而不是值对象,因为它可能是持久的并且具有唯一的标识。不管怎样,实体类使用服务类应该是完全没问题的。如果不需要在运行时根据外部配置选择所述服务的实现,那么我只需在客户端方法内实例化并使用所需的服务类。与某些人的想法相反,这并不排除单元测试,因为可以使用模拟工具进行隔离。
如果需要在运行时选择服务实现,则可以使用服务定位器。该模式可以为模拟/伪造提供直接支持,而不需要专门的模拟工具。使用支持注入“新”对象的 DI 框架将是另一种选择。
I would call
CreditCard
an entity rather than a value object, since it's likely to be persistent and have a unique identity.Anyway, it should be perfectly fine for an entity class to use service classes. If the implementations of said services don't need to be selected at runtime based on external configuration, then I would simply instantiate and use the desired service class inside the client method. Contrary to what some may think, this doesn't preclude unit testing, as a mocking tool can be used for isolation.
If the service implementation does need to be selected at runtime, than a Service Locator could be used. This pattern can provide direct support for mocking/faking, without the need for a specialized mocking tool. Use of a DI framework supporting injection into "newed" objects would be another alternative.