public class MoAttack { public void cityGateAsk() { Geli geli = new LiudeHua(); //引入革离角色接口 geli.responseAsk("墨者革离!");//通过接口开展剧情 } }
可是,我们可以看出MoAttack同时依赖于Geli接口和LiuDeHua类,并没有达到我们所期望的剧本仅依赖于角色的目的。但是角色最终必须通过具体的演员才能完成拍摄,如何让LiuDeHua和剧本无关而又能完成GeLi的具体动作呢?当然是在影片投拍时,导演将LiuDeHua安排在Geli的角色上,导演将剧本、角色、饰演者装配起来。 通过引入导演,剧本和具体的饰演者解耦了。对应到软件中,导演像是一个装配器,将具体的饰演者赋予了剧本的角色。 将这个例子对应于IoC。控制反转包括两个内容:其一是控制;其二是反转。控制是指选择GeLi角色扮演者的控制权;反转是指这种控制权从《墨攻》剧本中移除,转交到导演的手中。对于软件来说,即是某一接口具体实现类的选择控制权从调用类中移除,转交给第三方裁决。 因为IoC不够开门见山,Martin Folwler提出了DI(依赖注入:Dependency Injection)的概念用以代替IoC,即将调用类对接口实现类的依赖关系由第三方(容器或协作类)注入,以移除调用类对接口实现类的依赖。“依赖注入”这个名词显然比“控制反转”直接明了。 从注入方法上看,主要可以划分为三种类型:构造函数注入、属性注入和接口注入。Spring支持构造函数注入和属性注入。 构造函数注入 在构造函数注入中,我们通过调用类的构造函数,将接口实现类通过构造函数变量传入,如下: public class MoAttack { private Geli geli; public MoAttack(Geli geli) {//①注入革离的具体扮演者 this.geli = geli; } public void cityGateAsk() { geli.responseAsk("墨者革离!"); } } MoAttack的构造函数不关心具体是谁扮演革离这个角色,只要在①处传入的扮演者按照剧本要求完成角色功能即可。角色的具体扮演着由导演来安排,如下: public class Director { public void direct() { Geli geli = new LiuDeHua();//指定角色的扮演者 MoAttack moAttack = new MoAttack(geli);//注入具体扮演者到剧本中 moAttack.cityGateAsk();//开始“城门叩问”剧情的演出工作 } } 属性注入 有时候,导演会发现,虽然革离是影片《墨攻》的第一主角,但并非每个场景都需要革离的出现,在这种情况下通过构造函数注入并不妥当,这时可以考虑使用属性注入。属性注入可以有选择的通过setter方法完成调用类所需依赖的注入,更加灵活方便。 public class MoAttack { private Geli geli; public void setGeli(Geli geli){//属性注入方法 this.geli = geli; } public void cityGateAsk(){ geli.responseAsk("墨者革离"); } }
public class Director{ public void direct(){ GeLi geli = new LiuDeHua(); MoAttack moAttack = new MoAttack(); moAttack.setGeli(geli);//调用属性setter方法注入 moAttack.cityGateAsk(); } } 和通过构造函数注入革离扮演者不同,在实例化MoAttack时,并未指定任何扮演者,而是在实例化MoAttack后,调用其setGeli()方法注入扮演者。按照类似的方式,我们还可以分别为剧本中其他诸如梁王,巷淹中等角色提供注入的Setter方法,这样,导演就可以根据所拍剧段的不同,注入相应的角色了。
IOC(控制反转:Inverse of Control)《墨攻》有一个场景:当刘德华所饰演的墨者革离到达梁国都城下,城上梁国守军问道:“来者何人?”刘德华回答:“墨者革离!”我们不妨通过一个Java类为这个“城门叩问”的场景进行编剧,并借此理解IoC的概念:public class MoAttack { public void cityGateAsk(){ L