4

我正在使用 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中调用ServiceLocatorget onlyRepository1实例。(使用第一种方法会收到两个实例:forRepository1Repository2

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

  • 在第一种情况下,通过构造函数发送模拟对象。
  • 在第二种情况下,将 StructureMap 配置为在必要时使用模拟对象。

你认为?我违背了一些原则?(对不起我的英语)

4

2 回答 2

8

让我们先看看优势论点:

不必为每个存储库都有一个私有变量。

这是正确的。尽管实际上这 4 个字节作为参考通常并不重要。

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

我的看法正好相反。拥有一个构造函数会立即告诉您该类具有哪些依赖项。对于基类,您必须查看整个类以获取该信息。此外,如果你有一个低耦合、高内聚和没有缠结的好的设计,也无法使用工具来分析。而那些使用该类的人根本不知道该类具有哪些依赖项,除非他们阅读了实现。

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

应避免在一项服务中使用多个 repos,因为这会增加耦合。为所有服务提供所有 repos 是鼓励高耦合不良设计的最佳方式。所以我认为这是一个缺点。

该服务不会获得不必要的实例。例如,在ServiceA 中调用DoSomethingA 方法,ServiceLocator 只得到Repository1 实例。(使用第一种方法会收到两个实例:对于 Repository1 和 Repository2 )

使用完全不同的依赖项不同方法的服务是一个巨大的迹象,表明它不遵循单一责任原则。在这种情况下,它很可能会被拆分为两个服务。

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

在我看来,你走错路了。使用Service Locator 反模式,您将依赖项隐藏到使用您的类的人身上,这使得那些阅读实现的人更难看到该类具有哪些依赖项,并且您的测试不再被隔离。

于 2012-08-05T01:03:18.570 回答
1

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

于 2012-08-05T00:06:48.323 回答