我正在使用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
实例。 (使用第一种方法将收到两个实例:Repository1
和Repository2
)在这两种情况下,我都可以进行适当的测试:
答案 0 :(得分:7)
让我们先看看优势论点:
没有必要为每个存储库提供私有变量。
这是对的。虽然实际上参考的4个字节通常无关紧要。
我不需要服务的构造函数,使它们更小 更容易阅读。
我看到的恰恰相反。有一个构造函数会立即告诉您该类具有哪些依赖项。使用基类,您必须查看整个类以获取该信息。此外,它使得无法使用工具来分析您是否具有低耦合,高内聚和无缠结的良好设计。那些正在使用该类的人根本不了解该类所具有的依赖关系,除非他们阅读实现。
所有存储库都可以在任何服务中使用。
应避免在一个服务中使用多个repos,因为这会增加耦合。提供所有服务的所有回购是鼓励具有高耦合的糟糕设计的最佳方式。所以我认为这是一个劣势。
该服务不会获得不必要的实例。例如,呼入 ServiceA方法DoSomethingA仅为ServiceLocator获取 Repository1实例。 (使用第一种方法将获得两个 实例:用于Repository1和Repository2)
使用完全不同的依赖关系的服务不同的方法是一个巨大的迹象,它不遵循Single Responsibility Principle。在这种情况下,最有可能将它分成两个服务。
关于可测试性:在第二种情况下,通过使用单例(ServiceLocator),您的测试不再被隔离。所以他们可以相互影响。特别是在并行运行时。
在我看来,你走错了路。使用Service Locator anti-pattern,您将隐藏依赖关系到那些使用您的类的人,这使得阅读实现的人更难以查看该类具有哪些依赖关系,并且您的测试不再被隔离。
答案 1 :(得分:1)
我在这个设计中看到的问题是你将ServiceLocator与服务紧密耦合,因此不会促进松耦合。如果要使用mock / test存储库对服务进行单元测试,会发生什么?甚至可以换掉你的DI实现。在实践中,它可能不是一个大问题,因为您可以通过服务定位器配置您的测试服务,但它只是让我觉得代码味道。