C# 没有构造函数的依赖注入:真的是一种糟糕的做法吗?
我正在使用C#、MVC4、StructureMap等的web解决方案 在解决方案中,我为控制器提供了服务。举例来说:C# 没有构造函数的依赖注入:真的是一种糟糕的做法吗?,c#,asp.net-mvc,dependency-injection,structuremap,recommendation-engine,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){
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>();
}
}
通过第二种选择,我看到了某些优势:
- 没有必要为每个存储库都设置一个私有变量
- 我不需要服务的构造函数,使它们更小、更易于阅读
- 所有存储库都可以在任何服务中使用
- 该服务不会得到不必要的实例。例如,调用
方法ServiceA
服务定位器DoSomethingA
get only
实例。(使用第一种方法将收到两个实例:forRepository1
和Repository1
)Repository2
- 在第一种情况下,通过构造函数发送模拟对象李>
- 在第二种情况下,配置StructureMap以在必要时使用模拟对象
你认为呢?我违背了一些原则?(对不起,我的英文版)我看到这种设计的问题是,您将ServiceLocator与服务紧密耦合,因此不提倡松散耦合。如果要使用模拟/测试存储库对服务进行单元测试,会发生什么?或者甚至交换DI实现。实际上,这可能不是什么大问题,因为您可以通过服务定位器配置测试服务,但我觉得代码有点臭 让我们先看看优势参数: 没有必要为每个存储库都设置一个私有变量 没错。虽然在现实中,这4个字节作为参考通常并不重要 我不需要服务的构造器,使它们更小、更安全 更容易阅读 我的看法正好相反。拥有构造函数会立即告诉您类具有哪些依赖项。对于基类,您必须查看整个类才能获得这些信息。此外,如果您有一个低耦合、高内聚和无纠结的良好设计,则无法使用工具进行分析。而那些使用该类的人根本不知道该类有什么依赖关系,除非他们阅读了实现 所有存储库都可以在任何服务中使用 应该避免在一个服务中使用多个repo,因为这会增加耦合。向所有服务提供所有回购协议是鼓励高耦合的糟糕设计的最佳方式。所以我认为这是一个缺点 该服务不会得到不必要的实例。举个例子,打电话进来 ServiceA方法DoSomethingA ServiceLocator仅获取 Repository1实例。(使用第一种方法将收到两个 实例:用于Repository1和Repository2) 一个使用完全不同的依赖关系和不同方法的服务是一个巨大的迹象,表明它没有遵循标准。在这种情况下,它很可能会被分成两个服务 关于可测试性:在第二种情况下,通过使用单例(ServiceLocator),您的测试不再是孤立的。所以他们可以互相影响。尤其是在并行运行时
我认为你走错了路。使用时,您向使用您的类的人隐藏了依赖关系,这使得阅读实现的人更难看到该类具有哪些依赖关系,并且您的测试不再是孤立的。谢谢您!你的几行评论节省了我很多时间和头疼!你的意见和一个朋友给我的完全一样,只是想在这里问一下,因为我不太了解缺点。现在我看得更清楚了。非常感谢您抽出时间。您好。@remo gloor+1获取详细答案,但当您需要
暂时性依赖关系时,例如,如果服务需要像AWS S3
或类似的远程存储,则性能会出现一个问题。这将不必要地为所有控制器创建/处置实例,即使它们可能不会在单个请求范围内被调用。使用Get()
并用属性修饰类,使其具有该类所需的类型,并且Get()
将只返回修饰类型的实例。或者公共属性注入,那当然可以利用getter的优势来使用惰性加载。谢谢Ringo!你的答案和Remo的一样,只是有点短,所以我拨的是正确的,但都是正确的。问候语!
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();
}
}