Skip to content

Latest commit

 

History

History
101 lines (75 loc) · 5.14 KB

控制反转原理.md

File metadata and controls

101 lines (75 loc) · 5.14 KB

控制反转(IOC)

控制反转即IoC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。 所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。

IoC体现了好莱坞原则,即“不要打电话过来,我们会打给你”.

IoC是一个很大的概念,可以用不同的方式来实现。其主要实现方式有两种:

  1. 依赖查找(Dependency Lookup):容器提供回调接口和上下文环境给组件。EJB和Apache Avalon都使用这种方式。
  2. 依赖注入(Dependency Injection):组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。

后者是时下最流行的IoC类型,其又有接口注入(Interface Injection),设值注入(Setter Injection)和构造子注入(Constructor Injection)三种方式。

综上所述依赖注入属于控制反转的一种.

依赖注入之所以更流行是因为它是一种更可取的方式:让容器全权负责依赖查询,受管组件只需要暴露JavaBean的setter方法或者带参数的构造子或者接口,使容器可以在初始化时组装对象的依赖关系。 其与依赖查找方式相比,主要优势为:

  1. 查找定位操作与应用代码完全无关。
  2. 不依赖于容器的API,可以很容易地在任何容器以外使用应用对象。
  3. 不需要特殊的接口,绝大多数对象可以做到完全不必依赖容器。

什么是依赖

依赖是程序中常见的现象,比如类Car中用到了GasEnergy类的实例energy,通常的做法就是在Car类中显式地创建GasEnergy类的实例,并赋值给energy。如下面的代码

interface Energy {
      
}
  
class GasEnergy implements Energy {
      
}
  
class Car {
  Energy energy = new GasEnergy();
}

如代码中存在的问题有:

  • 类Car承担了多余的责任,负责energy对象的创建,这必然存在了严重的耦合性。举一个现实中的例子,一辆汽车使用哪种能源不是由汽车来决定,而是由汽车制造商(CarMaker)来决定,这是汽车制造商的责任。
  • 可扩展性,假设我们想修改能源为电动力,那么我们必然要修改Car这个类,明显不符合开放闭合原则。
  • 不利于单元测试。如果想测试Car类的的时候,你必须要保证GasEnergy实例是好的,而不能直接mock一个Energy的实例.

这些问题可以使用控制反转的技术来解决.

依赖注入

依赖注入的基本原则是:应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由IoC容器负责, “查找资源”的逻辑应该从应用组件的代码中抽取出来,交给IoC容器负责。

依赖注入是这样的一种行为,在类Car中不主动创建GasEnergy的对象,而是通过外部传入GasEnergy对象形式来设置依赖。常用的依赖注入有如下三种方式.

构造器注入

class Car {
  Energy mEnergy;
  public Car(Energy energy) {
      mEnergy = energy;
  }
}

Setter方法注入

class Car {
  Energy mEnergy;
      
  public void setEnergy(Energy energy) {
      mEnergy  = energy;
  }
}

接口注入

interface EnergyConsumerInterface {
  public void setEnergy(Energy energy);
}
  
class Car implements EnergyConsumerInterface {
  Energy mEnergy;
      
  public void setEnergy(Energy energy) {
      mEnergy  = energy;
  }
}

三种方式优缺点对比

  1. 接口注入。从注入方式的使用上来说,接口注入是现在不甚提倡的一种方式,基本处于“退 役状态”。因为它强制被注入对象实现不必要的接口,带有侵入性。而构造方法注入和setter 方法注入则不需要如此。
  2. 构造方法注入。这种注入方式的优点就是,对象在构造完成之后,即已进入就绪状态,可以 马上使用。缺点就是,当依赖对象比较多的时候, 构造方法的参数列表会比较长。而通过反 射构造对象的时候,对相同类型的参数的处理会比较困难,维护和使用上也比较麻烦。而且 在Java中, 构造方法无法被继承,无法设置默认值。对于非必须的依赖处理,可能需要引入多 个构造方法,而参数数量的变动可能造成维护上的不便。
  3. setter方法注入。因为方法可以命名,所以setter方法注入在描述性上要比构造方法注入好一些。 11 另外,setter方法可以被继承,允许设置默认值,而且有良好的IDE支持。缺点当然就是对象无法在构造完成后马上进入就绪状态。

综上所述,构造方法注入和setter方法注入因为其侵入性较弱,且易于理解和使用,所以是现在使 用最多的注入方式;而接口注入因为侵入性较强,近年来已经不流行了。

依赖查找

依赖查找和依赖注入一样属于控制反转原则的具体实现,不同于依赖注入的被动接受,依赖查找这是主动请求, 在需要的时候通过调用框架提供的方法来获取对象,获取时需要提供相关的配置文件路径、key等信息来确定获取对象的状态。