我已经使用了Dependency Injection一段时间了,现在我想向一组新开发人员谈谈IoC和DI.我记得亲自向一个人解释,他问我:
“为什么不使用:
private IMyInterface _instance = new MyImplementaion();
而不是经历所有的DI麻烦. “
我的回答是:“单元测试需要模拟和存根.” – 但是我们不在公司写单元测试,所以它没有说服他.我告诉他具体实现很糟糕,因为你紧密耦合到一个实现.更改一个组件将导致另一个组件发生变化
你能给出这样的代码的例子吗?
你能告诉我为什么这段代码不好的原因吗?
对我来说,我似乎很难解释它:-)
最佳答案 以下耦合的问题
public class MyClass
{
private IMyInterface _instance = new MyImplementation();
...
意味着任何时候创建MyClass(无论是直接创建还是由IoC容器创建)都会立即创建具体的MyImplementation并将其依赖_instance绑定到此具体实现.反过来,MyImplementation可能还有其他依赖项,这种依赖项也是这样耦合的.
类的解耦的好处使得MyClass仅依赖于其依赖关系的接口,而不依赖于依赖关系的具体实现(即SOLID principles的D)包括:
>对于单元测试 – 正如您所提到的,为了单独测试MyClass,使用新的依赖项,您需要求助于像Moles / Fakes这样的讨厌的东西来模拟硬连线的MyImplementation依赖项.
>对于替换 – 通过仅耦合到接口,您现在可以交换出IMyInterface的不同具体实现(例如,通过策略模式),而无需更改MyClass中的任何代码.
>使您的系统中的依赖关系显而易见,因为IMyInterface依赖关系可能还有其他依赖关系,需要解决(也可能需要配置注意事项).如果MyClass在内部隐藏了IMyInterface依赖关系,则调用者看不到MyClass的依赖关系.虽然在经典的1990年代的OO中这是常见的(即封装组合),但这可能使实现变得模糊,因为仍然需要完成所有依赖关系的部署.但是,在接口级别完成耦合(即MyClass的使用者仅通过IMyClass这样做),耦合可见接口是IMyClass,它将再次隐藏对IMyInterface的依赖,因为构造函数在接口上不可见).
>用于可配置的依赖生命周期控制.通过注入IMyInterface而不是新建MyImplementation,您可以在MyImplementation对象的生命周期管理方面允许其他配置选项.当MyImple的原始硬连线创建在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();