我目前在大多数开发人员都是面向对象的狂热者的环境中工作。 鉴于我们使用Java开发,所以我认为这是一件好事-保存狂热者的一部分。 特别是,我遇到了一个根深蒂固的模因 ,该模因指出无法对Rich Domain Objects建模和同时使用Spring依赖项注入。 这不仅是完全错误的,而且还表明缺乏对Spring功能的了解,我将在本文中尝试纠正这一功能。
但是,我的要点不是关于Spring,而是关于任何一种人们最喜欢的范式,无论是面向对象的编程,函数式编程,面向方面的编程还是其他。 这些仅是为了给软件提供所需的属性:可单元测试,可读的等等...因此,人们应该始终专注于这些属性,而不是获取它们的方式。 记得:
当智者指向月球时,白痴看着手指。
回到眼前的问题。 基本想法是让一些bean引用某些服务来做一些事情(注意这个想法背后的真实概念...),因此您可以调用:
someBean.doStuff()
以下正是不应该做的事情:
publicclassTopCaller{
@Autowired
privateStuffServicestuffService;
publicSomeBeannewSomeBean(){
returnnewSomeBeanBuilder().with(stuffService).build();
}
}
当new()
与TopCaller
和SomeBeanBuilder
类紧密耦合时,这种设计对于开发人员进行单元测试应该是一种厌恶。 无法在测试中对这种依赖关系进行存根,这会阻止createSomeBean()
测试createSomeBean()
方法。
您需要将SomeBean
原型注入SomeBeanBuilder
单例中。 这是方法注入,并且可以在Spring中借助查找方法来实现(我已经有一段时间写过博客了,您可能应该看看 )。
publicabstractclassTopCaller{
@Autowired
privateStuffServicestuffService;
publicSomeBeannewSomeBean(){
returnnewSomeBeanBuilder().with(stuffService).build();
}
publicabstractSomeBeanBuildernewSomeBeanBuilder();
}
使用正确的配置,每次调用newSomeBeanBuilder()
方法时,Spring都会提供一个不同的SomeBeanBuilder
。 通过这种新设计,我们将TopCaller
和SomeBeanBuilder
之间的强耦合变为了软耦合,可以在测试中进行存根并允许进行单元测试。
在当前设计中, SomeBeanBuilder
存在的唯一原因似乎是将StuffService
从TopCaller
到SomeBean
实例。 不需要通过方法注入来保持它。
有两种不同的可能改进:
- 鉴于我们的“新发现”知识,请注入:
- 方法
newSomeBean()
代替newSomeBeanBuilder()
进入TopCaller
-
StuffService
直接插入SomeBean
- 方法
- 将
StuffService
保留为TopCaller
属性,并在每次调用doStuff()
时将其传递
我倾向于第二种选择,因为我不赞成保留对单例服务的引用的域对象,除非每次调用都要传递许多参数。 与往常一样,没有一个最佳选择,只有上下文选择。
另外,我也将使用显式依赖项管理来代替一些自动的东西,但这是另一个争论。
我希望这篇文章毫无疑问地证明了Spring不会阻止Rich Domain Model,远非如此。 作为一般规则,了解使用的工具并按其方式使用,而不是仅仅为了它而遵循某些方法。
翻译自: https://blog.frankel.ch/rich-domain-objects-and-spring-dependency-injection-are-compatible/