我相信它增加代码复杂性和降低可维护性,我想知道是否有任何可行的替代品。
我不是在谈论从接口分离实现的概念,并且有一种从接口动态解析(递归)一组对象的方法。我完全支持。然而,传统的基于构造函数的做法似乎有几个问题。
1)所有测试依赖于构造函数。
在过去一年,在MVC 3 C#项目中广泛使用DI之后,我发现我们的代码充满了这样的东西:
public interface IMyClass { ... } public class MyClass : IMyClass { public MyClass(ILogService log,IDataService data,IBlahService blah) { _log = log; _blah = blah; _data = data; } ... }
问题:如果我的实现中需要另一个服务,我必须修改构造函数;这意味着这个类的所有单元测试都会中断。
即使与新功能无关的测试也需要至少重构来添加额外的参数和为该参数注入一个模拟。
这看起来像一个小问题,和自动化工具像resharper帮助,但它当然烦人,当一个简单的变化,这样导致100个测试打破。实际上,我已经看到人们做愚蠢的事情,以避免改变构造函数,而不是咬一颗子弹,并修复所有的测试,当这种情况发生。
public class MyWorker { public MyWorker(int x,IMyClass service,ILogService logs) { ... } }
创建这个类的实例,如果我在一个上下文中,如果给定的服务可用并已自动解决(例如控制器)或不幸地,通过将服务实例向下传递多个帮助类链的可能。
我看到这样的代码所有的时间:
public class BlahHelper { // Keep so we can create objects later var _service = null; public BlahHelper(IMyClass service) { _service = service; } public void DoSomething() { var worker = new SpecialPurposeWorker("flag",100,service); var status = worker.DoSomethingElse(); ... } }
如果示例不清楚,我所说的是将解析的DI接口实例向下通过多个层,除了底层,他们需要注入到某些东西。
如果一个类不依赖于一个服务,它应该依赖于该服务。这个想法,有一个“瞬态”依赖,一个类不使用服务,但简单地传递它,在我看来,废话。
但是,我不知道一个更好的解决方案。
有没有什么提供DI的好处没有这些问题?
public MyClass() { _log = Register.Get().Resolve<ILogService>(); _blah = Register.Get().Resolve<IBlahService>(); _data = Register.Get().Resolve<IDataService>(); }
有什么不好吗?
这意味着单元测试必须具有类的“先验知识”,以在测试初始化期间绑定正确类型的mock,但是我看不到任何其他缺点。
NB。我的例子是在c#,但我已经偶然在其他语言的同样的问题,特别是语言与不成熟的工具支持这些是主要的头痛。
让我们来看一下你的陈述:
All tests depend on the constructors.
[snip]
Problem: If I need another service in my implementation I have to modify the constructor; and that means that all of the unit tests for this class break.
做类依赖于一些其他服务是一个相当大的变化。如果你有几个服务实现相同的功能,我会认为有一个设计问题。正确的模拟和测试满足SRP(在单元测试中归结为“为每个测试用例编写单独的测试”)和独立应该解决这个问题。
2) Service instances get passed around unnecessarily,increasing code complexity.
DI的最常见的用途之一是将对象创建与业务逻辑分离。在你的情况下,我们看到你真正需要的是创建一些Worker,依次需要通过整个对象图注入的几个依赖。解决这个问题的最好方法是不要在业务逻辑中做任何新闻。对于这种情况,我宁愿注入一个工人工厂,从实际创建工人中抽象出商业代码。
I’ve contemplated using the DI framework inside the constructors instead as this resolves a couple of the problems:
06000
Is there any downside to doing this?
作为一个好处,你会得到使用Singleton模式(不可测试的代码和你的应用程序的巨大的状态空间)的所有缺点。
所以我会说,DI应该做正确的(像任何其他工具)。解决你的问题(IMO)在于理解DI和你的团队成员的教育。