当您还需要“默认”具体依赖项时,哪种是更好的依赖项注入模式?

时间:2018-11-09 01:45:01

标签: c# oop design-patterns dependency-injection

我已经使用了以下两种模式来实现可测试性,我想知道哪种OOP / SOLID更好。

模式1:提供两个构造函数,其中一个创建具体的依赖关系

public MyClass
{
    private IDependency dependency;

    public MyClass() : this(new ConcreteDependency()) { }

    public MyClass(IDependency dependency)
    {
       this.dependency = dependency;
    }
}

模式2:基类具有依赖关系初始化器,派生类以具体的依赖关系对其进行调用

public abstract MyClassBase
{
    private IDependency dependency;

    protected void Initialize(IDependency dependency)
    {
        this.dependency = dependency;
    }
}


public MyClass : MyClassBase
{
    public MyClass()
    {
       Initialize(new ConcreteDependency());
    }
}

2 个答案:

答案 0 :(得分:2)

Dependency Injection, Second Edition Principles, Practices, Patterns中所述,两个示例均提供了 Control Freak 反模式。

发生控制怪胎反模式:

  

您每次在Composition Root以外的任何地方都依赖 Volatile Dependency 。这违反了Dependency Inversion Principle

在第一个示例中,即使MyClass使用了IDependency抽象,它也会沿着对具体ConcreteDependency组件的引用进行拖动,导致这两个类紧密耦合。

在您的第二个示例中,发生了同样的紧密耦合,并且实际上是相同的反模式。此外,第二个示例甚至使用Initialize方法将依存关系应用于已经创建的MyClassBase实例。这会导致Temporal Coupling,它本身就是一种代码味道。

正确的解决方案可防止引起紧密耦合和时间耦合,这意味着您使用构造函数注入并仅定义一个构造函数

public MyClass
{
    private IDependency dependency;

    public MyClass(IDependency dependency)
    {
       if (dependency == null) throw new ArgumentNullException("dependency");
       this.dependency = dependency;
    }
}

答案 1 :(得分:0)

您的两种模式的不利之处在于它们可能会破坏Dependency Inversion PrincipleMyClass是更抽象的东西,但是知道ConcreteDependency是更具体的东西。确实:

  1. 无论何时重用MyClass类,ConcreteDependency类都必须可用。
  2. 此外,如果您需要将ConcreteDependency更改为ConcreteDependency2,则必须更改您的MyClass类,在某些情况下这是不好的,例如:有{ {1}}从未使用过默认构造函数的人。

更不用说,模式2可能会使您的对象处于意外状态,因为派生类可能会忘记调用MyClass方法,除非您的文档非常好。

我建议使用工厂模式:

Initialize