贺岁大片在中国已经形成了一个传统,每到年底总有多部贺岁大片纷至沓来让人应接
不暇。在所有贺岁大片中,张之亮的《墨攻》算是比较出彩的一部。该片讲述了战国时期
墨家人革离帮助梁国反抗赵国侵略的个人英雄主义故事,恢宏壮阔、浑雄凝重的历史场面
相当震撼。其中有一个场景:当刘德华所饰演的墨者革离到达梁国都城下,城上梁国守军
问到:“来者何人?”刘德华回答:“墨者革离!”我们不妨通过一个Java 类为这个“城门
叩问”的场景进行编剧,并借此理解IoC 的概念:
代码清单 3-1 MoAttack:通过演员安排剧本
public class MoAttack { public void cityGateAsk(){ //①演员直接侵入剧本 LiuDeHua ldh = new LiuDeHua(); ldh.responseAsk("墨者革离!"); } }
我们会发现以上剧本在①处,作为具体角色饰演者的刘德华直接侵入到剧本中,使剧
本和演员直接耦合在一起(图3-1)。
图 3-1 剧本和演员直接耦合
一个明智的编剧在剧情创作时应围绕故事的角色进行,而不应考虑角色的具体饰演
者,这样才可能在剧本投拍时自由地遴选任何适合的演员,而非绑定在刘德华一人身上。
通过以上的分析,我们知道需要为该剧本主人公革离定义一个接口:
代码清单 3-2 MoAttack:引入剧本角色
public class MoAttack { public void cityGateAsk() { //①引入革离角色接口 GeLi geli = new LiuDeHua(); //②通过接口开展剧情 geli.responseAsk("墨者革离!"); } }
在①处引入了剧本的角色——革离,剧本的情节通过角色展开,在拍摄时角色由演员
饰演,如②处所示。因此墨攻、革离、刘德华三者的类图关系如图3-2 所示:
图 3-2 引入角色接口后的关系
可是,从图3-2 中,我们可以看出MoAttack 同时依赖于GeLi 接口和LiuDeHua 类,
并没有达到我们所期望的剧本仅依赖于角色的目的。但是角色最终必须通过具体的演员才
能完成拍摄,如何让LiuDeHua 和剧本无关而又能完成GeLi 的具体动作呢?当然是在影片
投拍时,导演将LiuDeHua 安排在GeLi 的角色上,导演将剧本、角色、饰演者装配起来(图
3-3)。
图 3-3 剧本和饰演者解耦了
通过引入导演,使剧本和具体饰演者解耦了。对应到软件中,导演像是一个装配器,
安排演员表演具体的角色。
现在我们可以反过来讲解IoC 的概念了。IoC(Inverse of Control)的字面意思是控制
反转,它包括两个内容:
? 其一是控制
? 其二是反转
那到底是什么东西的“控制”被“反转”了呢?对应到前面的例子,“控制”是指选
择GeLi 角色扮演者的控制权;“反转”是指这种控制权从《墨攻》剧本中移除,转交到导
演的手中。对于软件来说,即是某一接口具体实现类的选择控制权从调用类中移除,转交
给第三方决定。
因为 IoC 确实不够开门见山,因此业界曾进行了广泛的讨论,最终软件界的泰斗级
人物Martin Fowler 提出了DI(依赖注入:Dependency Injection)的概念用以代替IoC,
即让调用类对某一接口实现类的依赖关系由第三方(容器或协作类)注入,以移除调用
类对某一接口实现类的依赖。“依赖注入”这个名词显然比“控制反转”直接明了、易于
理解。