即使使用DI,我们的业务/服务类型也需要在它们的方法中创建一些传递对象。这些传递对象总是值类型(代表纯数据)或I/O类型(表示外部状态)。值类型可以新建,但是我们希望在测试中模拟/存根的I/O类型,所以我们不能直接创建它们。
我看到的常见解决方案是给类某种IOFactory依赖:在生产过程中,我们为生成真实I/O对象的类提供一个工厂;在测试中,我们为生成假I/O对象的类提供一个工厂。
我不喜欢的是,不仅要创建模拟/存根I/O类型,还要为真正的类型及其替代品创建工厂。这感觉很累人,特别是在像JS这样的动态语言中,我常常可以轻松地为每个测试创建我的模拟/存根。
我想到的另一种方法是使用注射器,有点像服务定位器.
var file = injector.inject(File, '/path'); // given type, returns new instance of that type...such --在生产中,注入器被配置为提供一个真正的文件,而在测试中,它被配置为返回一个模拟/存根。我们可以将注入器看作一个特殊的全局,但可以说,注入器现在是每个需要使用它的业务类型的依赖项,因此应该像任何其他依赖项一样注入它。
我所看到的支持这一观点的主要论点是,在许多情况下,喷油器可以减少工厂样板(代价是额外的工厂配置工作)。反对的理由是什么?工厂是否更好,因为它们更具体地声明了类需要什么,从而充当文档?还是正确的解决方案完全不同?
发布于 2014-04-17 18:53:13
如何使用injector作为服务定位器
服务定位器有时被描述为反模式,因为:
(一些开发人员提出异议称其为反模式,但仍然普遍认为它有特定的用途,而且经常被滥用。)
您所描述的injector是反模式的主要示例。有了它,您的对象现在将有一个隐藏的所需的依赖项--它的构造函数中没有声明该依赖项。
如果在对象使用时未配置注入器,则会发生运行时错误。可能有人没有意识到,为了使对象正常工作,需要这种配置(您甚至可能是6个月后的某个人)。
依赖注入背后的思想是,对象非常明确地说明它需要什么才能按预期行事。其基本原理与接口maxim:容易正确使用;难以正确使用使用的原理相同。
您说得对,有时需要引入工厂才能动态实例化对象,这是非常麻烦的--但很多这种样板通常植根于许多OO语言冗长的语法中,而不一定是依赖注入的概念。
因此,为了短期的方便使用Service方法是可以的(就像任何其他全局变量一样)只要您意识到这是它在这种情况下真正提供的全部功能。
至于替代方法,请不要忘记,所需的依赖项不一定需要作为构造函数参数传递。
与将工厂传递给类的构造函数不同,有时使用工厂法方法是有意义的。也就是说,强制派生类提供依赖关系,而不是期望它来自对象的创建者。
如果可以使用有意义的默认依赖项(例如空对象)初始化SUT,则可以考虑在setter方法中注入该依赖项。
在C/C++中,开发人员有时甚至依赖链接器来处理依赖注入。詹姆斯·格伦宁在他的书“嵌入式C的测试驱动开发”中写道:
要打破对生产代码的依赖,只考虑协作者的接口。..。接口由头文件表示,实现由源文件表示。
LightSchedulerSUT在链接时绑定到生产代码实现。 单元测试通过提供替代实现来利用link。(临120)
最后,问问你希望从依赖注入中得到什么。如果它的好处并不超过实现它所涉及的工作,那么它可能是不必要的。但不要为了在短期内节省一点时间而回避它。
https://stackoverflow.com/questions/23122074
复制相似问题