依赖注入
没那么复杂
很多时候我们会将问题复杂化,而问题的答案却一如既往的简单。
public class Stupid{ protected IService Service { get; private set; } public Stupid() { Service = new ConcreteServiceA(); }}
public class Smart{ protected IService Service { get; private set; } public Smart(IService service) { Service = service; }}
上面是两段看上去极其相似的类型代码,区别在于 Service
属性的初始化。前者在创建类型实例的时候直接创建Service的实例,而后者则不同:
// 假设已经有一个 IService 的实例引用:concreteServiceAvar smart = new Smart(concreteServiceA);
这就是 依赖注入(Dependency Injection,DI)。
当然,这只是依赖注入的其中一种形式,因为我们至少听说过以下三种形式的依赖注入: 构造函数注入、 属性注入、 方法注入。没错,按照前面的思路,属性注入就是给属性赋值,方法注入就是用指定的参数调用方法 —— 他们都是传递一些东西(一般是另一个对象的引用)给某个对象。
是什么在误导我们
站在过高的角度上去看问题,难免会看不透问题的本质。
我们接触 依赖注入,往往起源于某个 依赖注入框架(NInject,Autofac,Castle,Unity......)。从那时起我们开始在 依赖注入框架 划定的高度上入手 依赖注入。容器?注册?解析?......依赖注入框架为我们提供了丰富强大的功能,同时也带来了一大堆概念。然之后,我们还是没搞清楚依赖注入是什么,有什么优势,甚至开始怀疑它的可用性。
可是,整个圈子都在夸赞和使用的东西,怎么可能没有利用价值呢? —— 把 依赖注入框架 当成 依赖注入,误导我们的,不是别人,是我们自己。
控制反转
说到依赖注入,就不得不提 控制反转(Inversion of Control,IoC):它不是具体技术,而是一种设计思想。
前文提到的 Stupid
类型,看上去也无伤大雅,很是美观易读。可是,IService
接口的设计初衷是什么呢? Service
属性值的创建已经由 Stupid
类型的构造器完成了,IService
接口为我们带来的 多态性 在这个类里面俨然不复存在;属性值将随着 Stupid
类型实例的消逝而被回收,也不可能再被重复利用。在设计上,我们将这种情况称为 紧耦合 —— IService
接口成了摆设,ConcreteServiceA
与 Stupid
二者已是密不可分的存在。
显然,这是不合理的,我们需要改变 —— 将 Service
属性值的创建和生命周期管理交由 Stupid
类以外的地方去控制,就像 Smart
类型那样。
对的,这就是 控制反转。跟依赖注入一样,没那么复杂。
控制反转(IoC) VS 依赖注入(DI)
事实上这两者并没有什么可比性。记住一句话即可:控制反转 是一种设计思想,而 依赖注入 是 控制反转 的实现方式之一。没错,之一,因为 IoC 的实现方式不只是 DI。