我已经使用了Dependency Injection一段时间了,现在我想向一组新开发人员讲述IoC和DI。我记得亲自向一个人解释,他问我:
“为什么不直接使用:
private IMyInterface _instance = new MyImplementaion();
而不是经历所有的DI麻烦。 “
我的回答是:“单元测试需要模拟和存根。” - 但是我们不在公司写单元测试,所以它没有说服他。我告诉他具体实现很糟糕,因为你紧密耦合到一个实现。更改一个组件将导致另一个组件发生变化。
你能举个例子吗? 你能告诉我为什么这段代码不好的原因吗?
对我而言,我似乎很难解释它: - )
答案 0 :(得分:2)
以下耦合的问题
public class MyClass
{
private IMyInterface _instance = new MyImplementation();
...
意味着任何时候创建MyClass
(无论是直接创建还是由IoC容器创建)都会立即创建具体的MyImplementation
并将其依赖项_instance
绑定到此具体实现。反过来,MyImplementation
可能还有其他依赖关系,它们也是以这种方式耦合的。
类的解耦的好处,使MyClass
仅依赖于其依赖关系的接口,而不依赖于具体实现(即SOLID principles的D)包括:
用于单元测试 - 正如您所提到的,为了单独测试MyClass
,使用new'ed
依赖项,您需要求助于Moles / Fakes
等令人讨厌的事情为了模拟硬连线MyImplementation
依赖。
替换 - 通过仅耦合到接口,您现在可以交换IMyInterface
的不同具体实现(例如,通过策略模式),而无需更改MyClass
中的任何代码。
使您的系统中的依赖关系显式且明显,因为IMyInterface
依赖关系可能具有进一步的依赖关系,需要解决(并且可能还需要配置注意事项)。如果MyClass
在内部隐藏IMyInterface
依赖关系,则调用者无法看到MyClass
的依赖关系。虽然在经典的1990年代的OO中这是常见的(即封装+组合),但这可能会掩盖实现,因为仍然需要完成所有依赖项的部署。但是,在接口级别完成耦合(即MyClass的使用者仅通过IMyClass
这样做),耦合可见接口为IMyClass
,这将再次隐藏对IMyInterface
的依赖,因为构造函数在界面上不可见。)
用于可配置的依赖生命周期控制。通过注入IMyInterface
而不是新建MyImplementation
,您可以在MyImplementation
对象的生命周期管理方面允许其他配置选项。在MyImplementation
上创建MyClass
的原始硬连线时,它实际上已经拥有MyImplementation
的生命周期。通过将其留给IoC容器,您现在可以使用MyImplementation
生命周期的其他选项,这可能更有效,例如如果MyImplementation
个实例是线程安全的,您可以选择在多个MyClass
实例之间共享实例。
总之,我认为重构应该看起来适合IoC构造函数依赖注入:
public class MyClass
{
// Coupled onto the the interface. Dependency can be mocked, and substituted
private readonly IMyInterface _instance;
public MyClass(IMyInterface instance)
{
_instance = instance;
}
...
IoC容器引导将定义需要绑定IMyInterface
的WHICH实现,并且还将定义依赖关系的生命周期,例如:在Ninject:
Bind<IMyInterface>()
.To<SomeConcreteDependency>() // Which implements IMyInterface
.InSingletonScope();