没有构造函数的依赖注入:真的是一个不好的做法?

时间:2012-08-04 23:30:19

标签: c# asp.net-mvc dependency-injection structuremap recommendation-engine

我正在使用C#,MVC4,StructureMap等进行Web解决方案。

在解决方案中,我有控制器服务。例如:

public class ServiceA{
    private readonly IRepository _repository1;
    private readonly IRepository _repository2;

    public ServiceA(IRepository1 repository1, IRepository2 repository2){
        _repository1=repository1;
        _repository2=repository2;
    }

    public void DoSomethingA(){
        _repository1.DoSomething();
    }

    public void DoSomethingB(){
        _repository2.DoSomething();
    }
}

public class ServiceB{
    private readonly IRepository _repository3;
    private readonly IRepository _repository4;

    public ServiceB(IRepository3 repository3, IRepository4 repository4){
        _repository3=repository3;
        _repository4=repository4;
    }

    public void DoSomethingA(){
        _repository3.DoSomething();
    }

    public void DoSomethingB(){
        _repository4.DoSomething();
    }
}

这样做是好的做法吗? :

public abstract class ServiceBase(){
    public IRepository1 Repository1 { get { return instanceOf<IRepository1>(); }}
    public IRepository2 Repository2 { get { return instanceOf<IRepository2>(); }}
    public IRepository3 Repository3 { get { return instanceOf<IRepository3>(); }}
    public IRepository4 Repository4 { get { return instanceOf<IRepository4>(); }}

    private T instanceOf<T>()
    {
        return ServiceLocator.Current.GetInstance<T>();
    }
}

然后以这种方式创建服务?

public class ServiceA : ServiceBase
{
    public void DoSomethingA(){
        Repository1.DoSomething();
    }

    public void DoSomethingB(){
        Repository2.DoSomething();
    }
}


public class ServiceB : ServiceBase
{
    public void DoSomethingA(){
        Repository3.DoSomething();
    }
    public void DoSomethingB(){
        Repository4.DoSomething();
    }
}

使用第二种选择,我看到了一些优势:

  • 没有必要为每个存储库提供私有变量。
  • 我不需要服务的构造函数,使它们更小,更容易阅读。
  • 所有存储库都可以在任何服务中使用。
  • 该服务不会获得不必要的实例。例如,在ServiceA方法DoSomethingA中调用ServiceLocator仅获取Repository1实例。 (使用第一种方法将收到两个实例:Repository1Repository2

在这两种情况下,我都可以进行适当的测试:

  • 在第一种情况下,通过构造函数发送模拟对象。
  • 在第二种情况下,配置StructureMap以在必要时使用模拟对象。
你觉得呢?我反对某些原则? (对不起,我的英文)

2 个答案:

答案 0 :(得分:7)

让我们先看看优势论点:

  

没有必要为每个存储库提供私有变量。

这是对的。虽然实际上参考的4个字节通常无关紧要。

  

我不需要服务的构造函数,使它们更小   更容易阅读。

我看到的恰恰相反。有一个构造函数会立即告诉您该类具有哪些依赖项。使用基类,您必须查看整个类以获取该信息。此外,它使得无法使用工具来分析您是否具有低耦合,高内聚和无缠结的良好设计。那些正在使用该类的人根本不了解该类所具有的依赖关系,除非他们阅读实现。

  

所有存储库都可以在任何服务中使用。

应避免在一个服务中使用多个repos,因为这会增加耦合。提供所有服务的所有回购是鼓励具有高耦合的糟糕设计的最佳方式。所以我认为这是一个劣势。

  

该服务不会获得不必要的实例。例如,呼入   ServiceA方法DoSomethingA仅为ServiceLocator获取   Repository1实例。 (使用第一种方法将获得两个   实例:用于Repository1和Repository2)

使用完全不同的依赖关系的服务不同的方法是一个巨大的迹象,它不遵循Single Responsibility Principle。在这种情况下,最有可能将它分成两个服务。

关于可测试性:在第二种情况下,通过使用单例(ServiceLocator),您的测试不再被隔离。所以他们可以相互影响。特别是在并行运行时。

在我看来,你走错了路。使用Service Locator anti-pattern,您将隐藏依赖关系到那些使用您的类的人,这使得阅读实现的人更难以查看该类具有哪些依赖关系,并且您的测试不再被隔离。

答案 1 :(得分:1)

我在这个设计中看到的问题是你将ServiceLocator与服务紧密耦合,因此不会促进松耦合。如果要使用mock / test存储库对服务进行单元测试,会发生什么?甚至可以换掉你的DI实现。在实践中,它可能不是一个大问题,因为您可以通过服务定位器配置您的测试服务,但它只是让我觉得代码味道。