IoC的概念——看到一篇写的通俗易懂文章

来源:互联网 发布:网络暴力电影 编辑:程序博客网 时间:2024/05/22 08:46

IOC(控制反转:Inverse of Control)


《墨攻》有一个场景:当刘德华所饰演的墨者革离到达梁国都城下,城上梁国守军问道:“来者何人?”刘德华回答:“墨者革离!”我们不妨通过一个Java类为这个“城门叩问”的场景进行编剧,并借此理解IoC的概念:

public class MoAttack {
  public void cityGateAsk(){
     LiuDeHua ldh = new LiuDeHua();
     ldh.responseAsk("墨者革离!");
  }
}

    我们会发现在以上剧本处,作为具体角色饰演者的刘德华直接侵入到剧本中,使剧本和演员直接耦合在一起。
    一个明智的编剧在剧情创作时应围绕故事的角色进行,而不应考虑角色的具体饰演者,这样才能在剧本投拍时自由的遴选任何适合的演员,而非绑定在刘德华一个人身上。通过以上的分析,我们知道需要为该剧本主人公革离定义个接口:

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方法,这样,导演就可以根据所拍剧段的不同,注入相应的角色了。

    通过容器完成依赖关系的建立
    虽然MoAttack和LiuDeHua实现了解耦,无须关注实现类的实例化工作,但这些工作在代码中依然存在,只是转移到Director中而已,可能导致导演的工作负荷过大。假设某一制片人想改变这一局面,在相中某个剧本后,通过一个“海选”或者第三中介机构来选择导演、演员,让他们各司其责,那剧本、导演、演员就都实现解耦了。
    所谓媒体“海选”和中介机构在程序领域即是一个第三方容器,它帮助我们完成类的初始化和装配工作,让我们从这些底层的实现类实例化、依赖关系装配等工作中脱离出来,专注于更有意义的业务逻辑开发工作,这无疑是一件令人向往的事情。Spring就是这样一个容器,它通过配置文件描述类之间的依赖关系,下面是Spring配置文件的对以上实例进行配置代码片段:
<beans>
   <bean id="geli" class="com.baobaotao.LiuDeHua"/>//显现类实例化
   <bean id="moAttack" class="com.baobaotao.MoAttack">
      <property name="geli" ref="geli"/>//建立依赖关系
   </bean>
</beans>
    通过new XmlBeanFactory("beans.xml")等方式即可启动容器。在容器启动时,Spring根据配置文件的描述信息,自动实例化Bean并完成依赖关系的建立,从容器中即可返回准备就绪的Bean实例,以待后续的使用。
    Spring为什么会有这种“神奇”的力量,仅凭一个简单的配置文件,就能魔法般的实例化并装配好程序所用的Bean呢?这种神奇的力量归功于Java语言本身的类反射功能跟。Java允许通过程序化的方式间接对Class进行操作,Class文件有类装载器装载后,在JVM中将形成一份描述Class的对象,通过该对象可以获知Class的结构信息,如构造函数、属性和方法等,并分别通过Java实例对这些信息进行描述。Java允许用户通过这个Class相关的描述对象来间接调用类的功能,这就为程序化操作Class文件提供了一个途径。 

转自:http://blog.sina.com.cn/s/blog_4cb4e877010096uo.html

0 0
原创粉丝点击