IoC控制反转理解

IoC(Inverse of Control),即控制反转,也叫做依赖注入。是面向对象设计的一个重要的概念。它提出了将被调用对象的控制权由调用者转移到第三方控制器上。这样以满足软件开发中的依赖倒置(DI)原则。
一个电影为例:
《墨攻》中一片段:守城士兵问刘德华扮演的墨者革离“来者何人?”,革离回答说“墨者革离”。现在考虑这样一个剧本:
class Action{
    public void gateAsk(){
        //演员直接侵入剧本
        LiuDeHua ldh=new LiuDeHua();
        ldh.responseAsk();
    }
}

可以看到,演员直接侵入了剧本之中,这样假设不再由刘德华扮演革离,那么剧本将不再适用。为此,我们实现一个革离的接口,通过接口展开剧情:
class Action{
    public void gateAsk(){
        //引入接口展开剧情
        GeLi geli=new LiuDeHua();
        geli.responseAsk();
    }
}
这样的Action、LiuDeHua、GeLi关系如下:
但是我们可以看到,LiuDeHua还是出现在剧本之中,脚本最终还是依赖于具体的扮演者。那么如何才能让Action脚本与具体的扮演者无关呢?这时候我们可以考虑一个导演,让他来控制GeLi的扮演者,即由导演决定GeLi的具体实现。如下:
此时,我们在不经意间,已经完成了控制反转:原本在脚本(调用者)之中就要给定具体的演员(被调用者),此时,我们将演员的决定权交由导演(第三方控制器)决定。以上便是控制反转的介绍。在实践中,我们通常有三种方式实现控制反转:
  1. 构造函数注入
  2. 属性注入
  3. 依赖注入
1.构造函数注入:
    在构造函数中将 接口的实现类以参数的方式传入。例如:
class Action{
    private GeLi geli;
    public Action(GeLi geli){
        this.geli=geli;
    }
    public void gateAsk(){
        //引入接口展开剧情
        GeLi geli=new LiuDeHua();
        geli.responseAsk();
    }
}

public class Director{
    GeLi geli=new LiuDeHua();
    Action action=new Action(geli);
    action.gateAsk();
}
但是存在一个弊端就是,演员刘德华在脚本开始拍摄时就一直在场。这显然是不可取的(因为这会占用计算机的内存)。所以一个好的方法是让演员在有戏的地方出现:
2.属性注入:
    其实就是通过一个getter和一个setter完成对相关接口的实现类的传入:

class Action{
    private GeLi geli;
    public Action(){}
    //属性注入
    public void setGeLi(GeLi geli){
        this.geli=geli;
    }
    public void gateAsk(){
        //引入接口展开剧情
        GeLi geli=new LiuDeHua();
        geli.responseAsk();
    }
}

public class Director{
    GeLi geli=new LiuDeHua();
    Action action=new Action();
    action.setGeLi(geli);
    action.gateAsk();
}
3.接口注入:
    其实就是就是将所有依赖注入的方法集合到一个接口中,然后通过实现接口方法来实现接口实现类的传入。
/**
 * ActorArrange接口
 */
public interface ActorArrange {
   public void setGeLi(GeLi geli);
}

class Action implements ActorArrange{
    private GeLi geli;
    public Action(){}
    //属性注入
    public void setGeLi(GeLi geli){
        this.geli=geli;
    }
    public void gateAsk(){
        //引入接口展开剧情
        GeLi geli=new LiuDeHua();
        geli.responseAsk();
    }
}

public class Director{
    GeLi geli=new LiuDeHua();
    Action action=new Action();
    action.setGeLi(geli);
    action.gateAsk();
}
    这样,我们成功地将控制权转移到了Director上,使得Action只专注于拍摄。但是总得工作量并没有减少啊!如何实现这些演员分配工作的自动化呢?由此,我们引入了第三方容器。它帮助完成类的初始化与装配工作,让开发者从这些底层实现类的实例化、依赖关系装配等工作中脱离出来,专注于更有意义的业务逻辑开发工作。

    本文转自http://stamen.iteye.com/blog/1489223/,在此感谢原著作者。

猜你喜欢

转载自blog.csdn.net/qq_23729557/article/details/71835476