2019独角兽企业重金招聘Python工程师标准>>>
Inversion of Control,即反转控制,或许说为依赖注入更为合适。IoC就是一种设计模式。
Interface Driven Design接口驱动,接口驱动有很多好处,可以提供不同灵活的子类实现,增加代码稳定和健壮性等等,但是接口一定是需要实现的,也就是如下语句迟早要执行:AInterface a = new AInterfaceImp(); 这样一来,耦合关系就产生了,如:
Class A{
AInterface a;
A(){}
aMethod(){
a = new AInterfaceImp();
}
}
ClassA与AInterfaceImp就是依赖关系,如果想使用AInterface的另外一个实现就需要更改代码了。当然我们可以建立一个Factory来根据条件生成想要的AInterface的具体实现,即:
InterfaceImplFactory{
AInterface create(Object condition){
if(condition = condA){
return new AInterfaceImpA();
}elseif(condition = condB){
return new AInterfaceImpB();
}else{
return new AInterfaceImp();
}
}
}
通过IoC模式可以彻底解决代码耦合,它把耦合从代码中移出去,放到统一的XML文件中,通过一个容器在需要的时候把这个依赖关系形成,即把需要的接口实现注入到需要它的类中,这可能就是“依赖注入”说法的来源了。
IOC模式,系统中通过引入实现了IOC模式的IOC容器,即可由IOC容器来管理对象的生命周期、依赖关系等,从而使得应用程序的配置和依赖性规范与实际的应用程序代码分开。其中一个特点就是通过文本的配件文件进行应用程序组件间相互关系的配置,而不用重新修改并编译具体的代码。
当前比较知名的IOC容器有:Pico Container、Avalon 、Spring、JBoss、HiveMind、EJB等。
在上面的几个IOC容器中,轻量级的有Pico Container、Avalon、Spring、HiveMind等,超重量级的有EJB,而半轻半重的有容器有JBoss,Jdon等。
可以把IoC模式看做是工厂模式的升华,可以把IoC看作是一个大工厂,只不过这个大工厂里要生成的对象都是在XML文件中给出定义的,然后利用Java 的“反射”编程,根据XML中给出的类名生成相应的对象。从实现来看,IoC是把以前在工厂方法里写死的对象生成代码,改变为由XML文件来定义,也就是把工厂和对象生成这两者独立分隔开来,目的就是提高灵活性和可维护性。
IoC中最基本的Java技术就是“反射”编程。反射又是一个生涩的名词,通俗的说反射就是根据给出的类名(字符串)来生成对象。这种编程方式可以让对象在生成时才决定要生成哪一种对象。反射的应用是很广泛的,象Hibernate、String中都是用“反射”做为最基本的技术手段。
在过去,反射编程方式相对于正常的对象生成方式要慢10几倍,这也许也是当时为什么反射技术没有普通应用开来的原因。但经SUN改良优化后,反射方式生成对象和通常对象生成方式,速度已经相差不大了(但依然有一倍以上的差距)。
IoC最大的好处是什么?因为把对象生成放在了XML里定义,所以当我们需要换一个实现子类将会变成很简单(一般这样的对象都是现实于某种接口的),只要修改XML就可以了,这样我们甚至可以实现对象的热插拨(有点象USB接口和SCIS硬盘了)。
IoC最大的缺点是什么?
(1)生成一个对象的步骤变复杂了(其实上操作上还是挺简单的),对于不习惯这种方式的人,会觉得有些别扭和不 直观。
(2)对象生成因为是使用反射编程,在效率上有些损耗。但相对于IoC提高的维护性和灵活性来说,这点损耗是微不足道的,除非某对象的生成对效率要求特别高。
(3)缺少IDE重构操作的支持,如果在Eclipse要对类改名,那么你还需要去XML文件里手工去改了,这似乎是所有XML方式的缺憾所在。
IOC实现初探
IOC关注服务(或应用程序部件)是如何定义的以及他们应该如何定位他们依赖的其它服务。通常,通过一个容器或定位框架来获得定义和定位的分离,容器或定位框架负责:
?保存可用服务的集合
?提供一种方式将各种部件与它们依赖的服务绑定在一起
?为应用程序代码提供一种方式来请求已配置的对象(例如,一个所有依赖都满足的对象),这种方式可以确保该对象需要的所有相关的服务都可用。
现有的框架实际上使用以下三种基本技术的框架执行服务和部件间的绑定:
?类型1 (基于接口): 可服务的对象需要实现一个专门的接口,该接口提供了一个对象,可以从用这个对象查找依赖(其它服务)。早期的容器Excalibur使用这种模式。
?类型2 (基于setter): 通过JavaBean的属性(setter方法)为可服务对象指定服务。HiveMind和Spring采用这种方式。
?类型3 (基于构造函数): 通过构造函数的参数为可服务对象指定服务。PicoContainer只使用这种方式。HiveMind和Spring也使用这种方式。
====================================================================================
IoC就是Inversion of Control,控制反转。在Java开发中,IoC意味着将你设计好的类交给系统去控制,而不是在你的类内部控制。这称为控制反转。
下面我们以几个例子来说明什么是IoC
假设我们要设计一个Girl和一个Boy类,其中Girl有kiss方法,即Girl想要Kiss一个Boy。那么,我们的问题是,Girl如何能够认识这个Boy?
在我们中国,常见的MM与GG的认识方式有以下几种
1 青梅竹马; 2 亲友介绍; 3 父母包办
那么哪一种才是最好呢?
青梅竹马:Girl从小就知道自己的Boy。
public class Girl {
void kiss(){
Boy boy = new Boy();
}
}
然而从开始就创建的Boy缺点就是无法在更换。并且要负责Boy的整个生命周期。如果我们的Girl想要换一个怎么办?(严重不支持Girl经常更换Boy,#_#)
亲友介绍:由中间人负责提供Boy来见面
public class Girl {
void kiss(){
Boy boy = BoyFactory.createBoy();
}
}
亲友介绍,固然是好。如果不满意,尽管另外换一个好了。但是,亲友BoyFactory经常是以Singleton的形式出现,不然就是,存在于 Globals,无处不在,无处不能。实在是太繁琐了一点,不够灵活。我为什么一定要这个亲友掺和进来呢?为什么一定要付给她介绍费呢?万一最好的朋友爱上了我的男朋友呢?
父母包办:一切交给父母,自己不用费吹灰之力,只需要等着Kiss就好了。
public class Girl {
void kiss(Boy boy){
// kiss boy
boy.kiss();
}
}
Well,这是对Girl最好的方法,只要想办法贿赂了Girl的父母,并把Boy交给他。那么我们就可以轻松的和Girl来Kiss了。看来几千年传统的父母之命还真是有用哦。至少Boy和Girl不用自己瞎忙乎了。
这就是IOC,将对象的创建和获取提取到外部。由外部容器提供需要的组件。
我们知道好莱坞原则:“Do not call us, we will call you.” 意思就是,You, girlie, do not call the boy. We will feed you a boy。
我们还应该知道依赖倒转原则即 Dependence Inversion Princinple,DIP
Eric Gamma说,要面向抽象编程。面向接口编程是面向对象的核心。
组件应该分为两部分,即 Service, 所提供功能的声明 Implementation, Service的实现
好处是:多实现可以任意切换,防止 “everything depends on everything” 问题.即具体依赖于具体。
所以,我们的Boy应该是实现Kissable接口。这样一旦Girl不想kiss可恶的Boy的话,还可以kiss可爱的kitten和慈祥的grandmother。
二、IOC的type
IoC的Type指的是Girl得到Boy的几种不同方式。我们逐一来说明。
IOC type 0:不用IOC
public class Girl implements Servicable {
private Kissable kissable;
public Girl() {
kissable = new Boy();
}
public void kissYourKissable() {
kissable.kiss();
}
}
Girl自己建立自己的Boy,很难更换,很难共享给别人,只能单独使用,并负责完全的生命周期。
IOC type 1,先看代码:代码
public class Girl implements Servicable {
Kissable kissable;
public void service(ServiceManager mgr) {
kissable = (Kissable) mgr.lookup(“kissable”);
}
public void kissYourKissable() {
kissable.kiss();
}
}
这种情况出现于Avalon Framework。一个组件实现了Servicable接口,就必须实现service方法,并传入一个ServiceManager。其中会含有需要的其它组件。只需要在service方法中初始化需要的Boy。
另外,J2EE中从Context取得对象也属于type 1。它依赖于配置文件。
IOC type 2:
public class Girl {
private Kissable kissable;
public void setKissable(Kissable kissable) {
this.kissable = kissable;
}
public void kissYourKissable() {
kissable.kiss();
}
}
Type 2出现于Spring Framework,是通过JavaBean的set方法来将需要的Boy传递给Girl。它必须依赖于配置文件。
IOC type 3:
public class Girl {
private Kissable kissable;
public Girl(Kissable kissable) {
this.kissable = kissable;
}
public void kissYourKissable() {
kissable.kiss();
}
}
这就是PicoContainer的组件 。通过构造函数传递Boy给Girl
PicoContainer container = new DefaultPicoContainer();
container.registerComponentImplementation(Boy.class);
container.registerComponentImplementation(Girl.class);
Girl girl = (Girl) container.getComponentInstance(Girl.class);
girl.kissYourKissable();
参考资料
1 http://www.picocontainer.org/presentations/JavaPolis2003.ppt
http://www.picocontainer.org/presentations/JavaPolis2003.pdf
2 DIP, Robert C Martin, Bob大叔的优秀论文
http://www.objectmentor.com/resources/articles/dip.pdf
3 Dependency Injection 依赖注射,Matrin Fowler对DIP的扩展
http://www.martinfowler.com/articles/injection.html
4 IOC框架
PicoContainer 优秀的IOC框架
http://picocontainer.org/
Avalon
http://avalon.apache.org/
Spring Framework
http://www.springframework.org/
HiveMind
http://jakarta.apache.org/commons/hivemind