c# – 为什么耦合到new关键字的依赖关系被认为是坏的?

我已经使用了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();
点赞