前言

在我编程生涯的学习过程中,这应该是第一次系统的学习一种理论,当时老师给出的概念很是抽象,甚至没有用任何图解和描述进行解释,而是仅仅作为考试填空的题目让我们死记硬背。如今静下心来去看资料和思考终归是有了一些理解。

为什么选择IOC

在程序设计的过程中,我们一致追求的是较低的耦合度。然而随着程序体量增大,过多的对象以及对象之间复杂的关系导致耦合度难以控制。
我在百度百科中找到了比较形象的图解,如果把对象比做齿轮,那么对象之间啮合的关系就像对象间的耦合关系:
image.png

从图中我们看到,不管是开发过程中还是后期维护中,我们不能任意的对其中的对象进行修改,否则可能会产生无法预料的后果。这显然不是一个好的设计模式。
从而,为了比较好的结偶,IOC思想应运而成。

什么是IOC

百度百科给出的解释是这样的:

控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

最为关键的在于最后两句,何为由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它?为什么叫做控制反转?

实现思路

我们通过前面未使用IOC的图例可以看到:我们不能挪动一个齿轮但不影响其他齿轮,即由于对象间的紧耦合,为了保证其他对象不受影响我们不能对对象进行任意的修改。而其中最主要的点就在于:我们无法实现让每个齿轮变成独立的。
于是IOC便帮助我们实现了这种想法:
image.png

可见IOC的实现思路是通过引入一个封装的中间件来“隔离”齿轮。
什么意思呢?在使用IOC之前可以看到,对象A和对象B,C都有直接的关联关系。我们无法绕过他们之间的联系而只考虑其一,但是使用IOC之后,可以看到和对象ABCD有直接关联的只有中间件,那么我们就可以专注于处理对象和对象与中间件之间的关系,而不用考虑封装的容器和对象之间的复杂耦合。

控制反转

顾名思义,就是把控制权交给对方。这确实与IOC所要表达的东西相似。
软件系统在没有引入IOC容器之前,如图1所示,对象A依赖于对象B,那么对象A在初始化或者运行到某一点的时候,自己必须主动去创建对象B或者使用已经创建的对象B。无论是创建还是使用对象B,控制权都在自己手上。

软件系统在引入IOC容器之后,这种情形就完全改变了,如图3所示,由于IOC容器的加入,对象A与对象B之间失去了直接联系,所以,当对象A运行到需要对象B的时候,IOC容器会主动创建一个对象B注入到对象A需要的地方。

这里我看到过一个写的非常清楚的解释:在这里记录一下:

●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。  

●为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。

IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

依赖注入(DI)

怎样理解依赖注入?刚才提到,我们把控制权:即对象的创建和调用的职能给了IOC容器,当一个程序或对象需要什么时,就由IOC容器负责进行创建查找并注入。这个过程中,请求方完全没有在意如何实现对资源和对象的获取,而单纯只是依赖于IOC容器。
如此看来DI的提出好像对于IOC来说并没有什么太大的进步,然而依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
同样引用一段我看到的理解片段:

理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:
  ●谁依赖于谁:当然是应用程序依赖于IoC容器;
  ●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
  ●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
  ●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。

小结

总而言之,通过第三方的引入实现了松散耦合,并且极大的简化了开发过程中的依赖关系。从此我们进行设计时可以专注于对象以及功能设计

Q.E.D.


二八笙歌云幕下,三千世界雪花中