依赖倒置
依赖倒置
High level modules should not depends upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.
高层模块不应该依赖于低层模块,它们应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。应该面向接口编程,不应该面向实现类编程。面向实现类编程,相当于就是论事,那是正向依赖(正常人思维);面向接口编程,相当于通过事物表象来看本质,那是反向依赖,即依赖倒置(程序员思维)。并不是说,所有的类都要有一个对应的接口,而是说,如果有接口,那就尽量使用接口来编程吧。
依赖注入与控制反转
Martin Fowler 在 2004 年发表的 Inversion of Control Containers and the Dependency Injection pattern 一文中阐述了 IoC 的概念与实践模式。
在传统的层次化模式(Layers Pattern)中,高层次的组件(Higher Level)调用低层次(Lower Level)的组件来逐步构建复杂的系统;不过这种方式会导致组件之间存在较强的耦合,对于低层次组件的强依赖往往也会限制了高层次组件的可扩展性与重用性。
通常我们在没有依赖注入的时候如果 A 依赖于 B,那么在 A 初始化或者执行中的某个过程需要先创建 B,这时我们就认为 A 对 B 的依赖是正向的。但是这样解决依赖的办法会得得 A 与 B 的逻辑耦合在一起,依赖越来越多代码就会变的越来越糟糕。如下图所示,齿轮之间是相互依赖的,一损俱损。控制反转(IOC)模式就是要解决这个问题,它会多引入一个容器(Container)的概念,让一个 IOC 容器去管理 A、B 的依赖并初始化。当我们去掉容器时,剩下的齿轮成了一个个独立的功能模块。
IoC
IoC(Inversion of Control),即控制反转;在开发中,IoC 意味着你设计好的对象交给容器控制,而不是使用传统的方式,在对象内部直接控制。
-
谁控制谁,控制什么:在传统的程序设计中,我们直接在对象内部通过 new 的方式创建对象,是程序主动创建依赖对象;而 IoC 是有专门一个容器来创建这些对象,即由 IoC 容器控制对象的创建;谁控制谁?当然是 IoC 容器控制了对象;控制什么?主要是控制外部资源获取。
-
为何是反转了,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转了;哪些方面反转了?依赖对象的获取被反转了。
IoC 不是一种技术,只是一种思想,一个重要的面向对象编程法则,它能指导我们如何设计松耦合、更优良的系统。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了 IoC 容器后,把创建和查找依赖对象的控制权交给了容器,由容器注入组合对象,所以对象之间是松散耦合,这样也便于测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
DI
DI - Dependency Injection,即"依赖注入":组件之间的依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
理解 DI 的关键是:“谁依赖了谁,为什么需要依赖,谁注入了谁,注入了什么”,那我们来深入分析一下:
- 谁依赖了谁:当然是应用程序依赖 IoC 容器
- 为什么需要依赖:应用程序需要 IoC 容器来提供对象需要的外部资源
- 谁注入谁:很明显是 IoC 容器注入应用程序依赖的对象
- 注入了什么:注入某个对象所需的外部资源(包括对象、资源、常量数据)
IoC 和 DI 其实它们是同一个概念的不同角度描述,由于控制反转的概念比较含糊,所以 2004 年 Martin Fowler 又给出了一个新的名字:“依赖注入”,相对 IoC 而言,“依赖注入” 明确描述了被注入对象依赖 IoC 容器配置依赖对象。
总的来说,控制反转(Inversion of Control)是说创建对象的控制权发生转移,以前创建对象的主动权和创建时机由应用程序把控,而现在这种权利转交给 IoC 容器,它就是一个专门用来创建对象的工厂,你需要什么对象,它就给你什么对象。有了 IoC 容器,依赖关系就改变了,原先的依赖关系就没了,它们都依赖 IoC 容器了,通过 IoC 容器来建立它们之间的关系。