依赖倒置、控制反转和依赖注入的区分
依赖倒置、控制反转和依赖注入的区分依赖倒置(Dependency Inversion Principle)、控制反转(Inversion of Control)和依赖注入(Dependency Injection)从思想来讲是统一的,或者说是类似的,有人也说它们是同一个东西。但是还是可以做一点区分:
依赖倒置原则是进行软件设计时考虑遵循的一个原则。具体为:
(1)上层模块不应该依赖于下层模块,它们共同依赖于一个抽象。
(2)抽象不能依赖于具象,具象依赖于抽象。
控制反转
是软件运行时体现出来的一个特征:如果对象A运行时依赖于对象B,但A并不去创建B,而是从外界直接取得B。
也就是说,一个对象并不是自己去创建它所依赖的其它对象。
依赖注入是控制反转的一种实现手段。
如上面的例子,B的取得并不需要A的干涉,而是利用某些框架在通过构造参数或属性设置来实现。
IOC:Inversion of Control 控制反转
DI:Dependency Injection 依赖注入
控制反转,从字面意思来看,就是控制权又被动变主动,最后又变回被动。
举个例子:
你的主管要求你做一件事情,这个时候就存在这么几个过程:
- 主管命令你做事情(这个时候主动权在主管,你是被动的)
- 你接到命令做事情(这个时候主题是你,你是主动的,控制权在你手里)
- 你完成事情(这个时候主题依然是你,控制权在你手里)
- 报告主管做完事情(主动权又叫交到主管手里了)
上面的整个过程就完成了一次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=newB();
b.DoMethod();
}
}
假如在Main函数中如下执行:
A a=newA();
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,这个方法好像涉及到的很少。
DI,依赖注入,从字面意思就可以看出,依赖是通过外接注入的方式来实现的。这就实现了解耦,而DI的方式通常有三种,
- 构造器注入
- 属性设置器注入
- 接口注入(我感觉接口注入是同时存在于上两种注入方式的,而不应该独立出来)
以上的阐述只是为了先让我们能对IOC和DI有一个感性的理解,那么IOC真正解决的问题是什么呢?
我们讲了那么多主动被动的问题,那我们是从什么视角来看待这个问题的呢?
所谓为什么你是主动,而我不是主动呢?这就需要一个参照物,那这个参照物是什么呢?就是容器,在容器中来体现主动和被动。
用白话来讲,就是由容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念所在:控制权由应用代码中转到了外部容器,控制权的转移,是所谓“反转”,这是通常对IOC的一个解释。
从容器的角度来看主动和被动,和由容器来控制程序之间的关系,应该是相通的,是一个意思。
IOC要解决的就是程序之间调用的一个问题,它应该是一个思想层面的东西,是一个中心,就像一支乐队的指挥,而程序就是乐器,通过指挥来协调各种乐器,来演奏出美好的音乐来。
以下文字参考:http://www.cnblogs.com/gooddasenlin/archive/2009/03/02/1401631.html
Interface Driven Design 接口驱动
接口驱动有很多好处,可以提供不同灵活的子类实现,增加代码稳定和健壮性等等,但是接口一定是需要实现的,也就是如下语句迟早要执行:AInterface a = new AInterfaceImp(); 这样一来,耦合关系就产生了。
如:
Class A {
AInterface a;
A() {}
aMethod() {
a=newAInterfaceImp();
}
}
ClassA与AInterfaceImp就是依赖关系,如果想使用AInterface的另外一个实现就需要更改代码了。
当然我们可以建立一个Factory来根据条件生成想要的AInterface的具体实现,即:
InterfaceImplFactory {
AInterface create(Object condition) {
if(condition=condA) {
returnnewAInterfaceImpA();
} elseif(condition=condB) {
return new AInterfaceImpB();
} else {
return new AInterfaceImp();
}
}
}
表面上是在一定程度上缓解了以上问题,但实质上这种代码耦合并没有改变。
通过IoC模式可以彻底解决这种耦合,它把耦合从代码中移出去,放到统一的XML文件中,通过一个容器在需要的时候把这个依赖关系形成,即把需要的接口实现注入到需要它的类中,这可能就是“依赖注入”说法的来源了。
IOC模式系统中,通过引入实现IOC模式的IOC容器,即可由IOC容器来管理对象的生命周期、依赖关系等,从而使得应用程序的配置和依赖性规范与实际的应用程序代码分开。其中一个特点就是通过文本的配置文件进行应用程序组件间相互关系的配置,而不用重新修改并编译具体的代码。
当前比较知名的IOC容器有:Pico Container、Avalon 、Spring、JBoss、HiveMind、EJB等。其中,轻量级的有Pico Container、Avalon、Spring、HiveMind等,超重量级的有EJB,而半轻半重的有容器有JBoss,Jdon等。
可以把IoC模式看做是工厂模式的升华,可以把IoC看作是一个大工厂,只不过这个大工厂里要生成的对象都是在XML文件中给出定义的,然后利用Java 的“反射”编程,根据XML中给出的类名生成相应的对象。从实现来看,IoC是把以前在工厂方法里写死的对象生成代码,改变为由XML文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。
IoC中最基本的Java技术就是“反射”编程。反射又是一个生涩的名词,通俗的说反射就是根据给出的类名(字符串)来生成对象。这种编程方式可以让对象在生成时才决定要生成哪一种对象。反射的应用是很广泛的,象Hibernate、String中都是用“反射”做为最基本的技术手段。
IoC最大的好处是什么?因为把对象生成放在了XML里定义,所以当我们需要换一个实现子类将会变成很简单(一般这样的对象都是现实于某种接口的),只要修改XML就可以了。
参考:http://www.cnblogs.com/niuniu1985/archive/2010/01/13/1646375.html