2009年7月29日星期三

[转]对控制反转(IOC)和依赖注入(DI)的理解

×××××××××××××首先说一下什么是IOC和DI,IOC是Inversion of Control(控制反 转)的简写,DI是Dependency Injection(依赖注入)的简写,martinfowler对IOC的解释为:“Inversion of control is a common characteristic of frameworks, so saying that these lightweight containers are special because they use inversion of control is like saying my car is special because it has wheels.”

  我想对这一概念进行一个个人的阐述,以方便我的理解。控制反转,从字面意思来看,就是控制权由被动变主动又变为被动,或被动变主动又变为被动。从这个角度来说,IOC就变得非常容易理解了。

  举个例子:你的主管要求你做一件事情,这个时候就存在这么几个过程,

  主管命令你做事情(这个时候主动权在主管,你是被动的)

  你接到命令做事情(这个时候主题是你,你是主动的,控制权在你手里)

  你完成事情(这个时候主题依然是你,控制权在你手里)

  报告主管做完事情(主动权又叫交到主管手里了)

  上面的整个过程就完成了一次IOC,从上面可以看出,IOC的基本思想是控制权的转换过程。

  举个代码的例子:

 假如有Class A,Class B,在A内部会初始化一个B,调用B的一个方法DoMethod

public Class B

  {

  public void DoMethod()

  {

  /// do somthing;

  }

  }

  public Class A

  {

  public void Excute()

  {

  B b = new B();

  b.DoMethod();

  }

  }

  假如在Main函数中如下执行:

  A a = new A();

  a.Excute();

  从这两行代码来看,事实上也存在一个IOC的过程,a——>b——>a,理解的关键点就在在A的内部调用Excute的时候,方法b.DoMethod的执行。

 理解了IOC,我们再看一下DI,从上面A调用B我们可以看出,在初始化一个A的实例时,也必须实例化一个B,也就是说如果没有B或者B出了问题,A就无法实例化,这就产生了一种依赖,就是A依赖B,这种依赖从设计的角度来说就是耦合,显然它是无法高内聚低耦合的要求的。这个时候就需要解耦,当然解耦有很多种方法,而DI就是其中一种。不管任何一种解耦方法,都不是说使A和B完全没有关系,而是把这种 关系的实现变得隐晦,不那么直接,但是又很容易实现,而且易于扩展,不像上面的代码那样,直接new一个B出来。那为什么我们总是把IOC和DI联系到一 起呢?是因为DI的基本思想就是IOC,而体现IOC 思想的方法还有另外一个,那就是Service Locator,这个方法好像涉及到的很少。

××××××××××××××××××××IoC的几种实现类型

(1)Type1接口注入

通常做法是利用接口将调用者与实现者分离。
public class Sport {
private InterfaceBall ball; //InterfaceBall是定义的接口
public void init() {
//Basketball实现了InterfaceBall接口
ball = (InterfaceBall) Class.forName("Basketball").newInstance();
}
}
Sport类在编译期依赖于InterfaceBall的实现,为了将调用者与实现者分离,我们动态生成Basketball类并通了强制类型转换为InterfaceBall。Apache Avalon是一个典型的Type1型IoC容器。

(2)setter方法注入

在类中暴露setter方法来实现依赖关系。
public class Sport {
private InterfaceBall ball;
public void setBall(InterfaceBall arg) {
ball = arg;
}
}
这种方式对已经习惯了JavaBean的程序员而言,更显直观。Spring就是实现了该类型的轻量级容器。

(3)Type3构造子注入

即通过构造方法完成依赖关系。
public class Sport {
private InterfaceBall ball;
public Sport(InterfaceBall arg) {
ball = arg;
}
}
可以看到,通过类的构造方法建立依赖关系。由于Type3在构造期就形成了对象的依赖关系,即存对象的重用变的困难。有些框架需要组件提供一个默认的构造 方法,此时就体现了Type3的局限性。通常所有的参数都是通过构造方法注入的,当对象间的依赖关系较多时,构造方法就显的比较复杂,不利于单元测试。 PicoContainer就是实现了Type3依赖注入模式的轻量级容器。

  DI,依赖注入,从字面意思就可以看出,依赖是通过外接注入的方式来实现的。这就实现了解耦,而DI的方式通常有三种,

  构造器注入

  属性设置器注入

 接口注入(我感觉接口注入是同时存在于上两种注入方式的,而不应该独立出来)

 以 上的阐述只是为了先让我们能对IOC和DI有一个感性的理解,那么IOC他真正解决的问题是什么呢?我们讲了那么多主动被动的问题,那我们是从什么视角来 看待这个问题的呢?所谓为什么你是主动,而我不是主动呢?这就需要一个参照物,那这个参照物是什么呢?就是容器,在容器中来体现主动和被动。“用白话来 讲,就是由容器控制程序之 间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓”控制反转“的概念所在:控制权由应用代码中转到了外部容器,控制权的转移,是所谓反转", 这是通常对IOC的一个解释。从容器的角度来看主动和被动,和由容器来控制程序之间的关系,应该是相通的,是一个意思。到这里我们就应该基本明白 了,IOC要解决的就是程序之间调用的一个问题,它应该是一个思想层面的东西,是一个中心,就像一支乐队的指挥,而程序就是乐器,通过指挥来协调各种乐 器,来演奏出美好的音乐来。

没有评论: