分享
 
 
 

IoC模式的类型及其实现

王朝java/jsp·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

1引言

面向对象的思想已经深入人心,但是要利用面向对象的思想开发出优秀的应用程序却不是一件容易的事情。正是基于面向对象的思想,人们对各种应用程序进行了大量的分析、总结、归纳出了设计模式。Alexanders给出模式的经典定义是:每个模式都描述了一个在我们的环境中不断出现的问题,然后描述了该问题的解决方案的核心。通过这种方式,你可以无数次地使用那些已有的解决方案,无需再重复相同的工作[2]。设计模式技术在GoF的经典书籍面世以来,得到了广泛的关注、研究与应用。时下如雨后春笋般涌现的各种框架都是利用设计模式的典范。其中IoC模式更是广泛应用于各种框架中。

2 IoC模式

2.1 IoC模式简介

IoC(Inversion of Control)模式并不是什么新的东西,它是一种很普遍的概念,GoF中的Template Method 就是IoC的结构。顾名思义,IoC即控制反转。著名的好莱坞原则:“Don’t Call us, We will call you”,以及Robert C. Martin在其敏捷软件开发中所描述的依赖倒置原则(Dependency Inversion Principle, DIP)都是这一思想的体现。依赖注入(Dependency Injection)是Martin Flower对IoC模式的一种扩展的解释[2]。IoC是一种用来解决组件(实际上也可以是简单的Java类)之间依赖关系、配置及生命周期的设计模式,其中对组件依赖关系的处理是IoC的精华部分。IoC的实际意义就是把组件之间的依赖关系提取(反转)出来,由容器来具体配置。这样,各个组件之间就不存在hard-code的关联,任何组件都可以最大程度的得到重用。运用了IoC模式后我们不再需要自己管理组件之间的依赖关系,只需要声明由容器去实现这种依赖关系。就好像把对组件之间依赖关系的控制进行了倒置,不再由组件自己来建立这种依赖关系而交给容器(例如我们后面会介绍的PicoContainer、Spring)去管理。

我们从一个简单的例子看起,考虑一个Button控制Lamp的例子:

public class Button {

private Lamp lamp;

public void push() {

lamp.turnOn();

}

}

但是马上发现这个设计的问题,Button类直接依赖于Lamp类,这个依赖关系意味着当Lamp类修改时,Button类会受到影响。此外,想重用Button类来控制类似与Lamp的(比如同样具有turnOn功能的Computer)另外一个对象则是不可能的。即Button控制Lamp,并且只能控制Lamp。显然违反了“高层模块不应该依赖于低层模块,两者都应该依赖于抽象;抽象不应该依赖于具体实现,细节应该依赖于抽象” 这一原则(DIP原则)。考虑到上述问题,自然的想到应该抽象出一个接口SwitchableDevice,来消除Button对Lamp的依赖,于是设计如下:

public class Button {

private SwitchableDevice lamp;

public Button(){

lamp= new Lamp();

}

}

再深入考虑一下,虽然我们的Button现在可以控制实现了SwitchableDevice接口的Computer,但是Button和Lamp类之间还是存在create这样的依赖关系。为了解决这种依赖关心,经典的GoF模式就是采用Factory模式,将对象的创建交给Factory类来创建,但是这种创建仍是显示的,组件变化了仍然需要重新编译程序。而采用J2EE经典的service locator模式,如果你要把Button组件拿到另一个系统里面用,你就必须修改它的源码,让它使用另一个系统的serviceLocator。换句话说,这个组件不具备可移植性。这就是需要依赖注入的道理,让组件的创建、配置及生命周期总是由外部容器来管理。

2.2 IoC的类型

2.2.1 IoC的类型

在介绍如何利用IoC模式实现彻底解耦之前,我们先看看IoC的类型:

2.2.1.1 Method-based (M) IoC

在每个方法调用中传递其依赖的组件。如果方法需要某个组件,就把该组件作为参数传递给方法。

2.2.1.2 Interface-based (I) IoC (Type 1)

使用接口如Serviceable, Configurable 等等,来声明依赖。EJB容器就是一个Type1的重量级容器,部署在它内部的EJB组件使用接口来声明依赖关系。

2.2.1.3 Setter-based (S) IoC (Type 2)

使用setters 来设置依赖组件。把依赖的组件作为一个属性,通过setters方法来动态设置依赖组件。

2.2.1.4 Constructor-based (C) IoC (Type 3)

使用构造函数来声明依赖。通过传递组件参数到构造函数中,来实现依赖关系。

2.2.2 IoC类型的比较

这几种类型中,type 3侵入性较小。因为在面向对象的理论里,constructor并不是对象契约的一部分。按照Bertrand Meyer的说法,你永远不应该直接调用constructor,因为这就意味着client代码与实现(而非契约)绑定在一起。那么,既然constructor并不属于对象契约的一部分,在constructor里暴露元信息就不会影响对象契约。Type 2虽然也很好,但setter毕竟属于对象契约,把一个setter用于IoC多少有一点“破坏性”,而且通过setter方法过多的暴露了内部对象的内部细节,这就失去了对象的封装。

Type 2很合适的作为应用程序的bean工厂。如果是更多的动态组装,可能type 3更好一点。从定义上来说,type 2是基于setter的,type 3是基于constructor的。为什么说type 2更适合于做bean工厂呢?因为setter是各个分离的,对于有定义的n个setter,bean工厂调用其中的0~n个都是合法的。而type 3则稍微有点麻烦,不能适应依赖较多的情况,组件的“元信息”在constructor的参数列表中体现,你必须一次性提供所有必要的参数。如果需要很多组件,就需要在构造函数中传递很多参数,这样会导致constructor的参数过多过长。

2.3 IoC容器

根据容器对组件的侵入的程度,可以把IoC容器分为以下三类:

2.3.1 Interface Injection

对应Type 1 IoC ,使用接口来声明依赖。这类IoC容器侵入性最强,需要通过上下文来获取组件.组件需要实现容器提供的特定接口,这样,组件的重用就被限定在该容器内。这类容器的代表有Apache Avalon。Avalon 不怎么流行,尽管它很强大而且有很长的历史。Avalon属于重量级容器,并且看起来比新的IoC解决方案更具侵入性。

2.3.2 Setter Injection

对应Type 2 IoC ,使用setters来设置依赖组件。这类IoC容器需要组件提供accessor方法,依赖关系通过setter方法来注入。按照java组件模型,一般的javabean都会有accessor方法,因此组件的重用性没有任何限制。这类容器的代表有Spring,同时它也实现了第三类IoC容器。Spring是一个非常活跃的、优秀的开源项目。它是一个基于IoC和AOP(Aspect-Oriented Programming,面向方面编程)的构架多层J2EE系统的框架,它优雅的实现了MVC框架,支持使用可声明事务管理(declarative transaction management)。更重要的是Spring框架的无侵入性[3]。

2.3.3 Constructor Injection

对应Type 3 IoC ,使用构造函数来声明依赖。这类IoC容器需要组件由构造方法来配置依赖关系。和第二种IoC类型类似,组件重用没有任何问题。并且Constructor Injection更加严格,完全按照契约(contract)来配置组件依赖。这类容器的代表有PicoContainer。

PicoContainer是一个轻量级而且更强调通过构造函数表达依赖性,而不是JavaBean 属性。 与Spring不同,它的设计允许每个类型一个对象的定义(可能是因为它拒绝任何Java代码外的元数据导致的局限性)。

2.4 利用IoC容器实现控制反转

下面我们就来看看如何利用IoC容器PicoContainer实现本文开始处举的例子,主要代码如下:

private MutablePicoContainer configureContainer() {

MutablePicoContainer pico = new DefaultPicoContainer();

pico.registerComponentImplementation(SwitchableDevice.class, Lamp.class);

pico.registerComponentImplementation(Button.class);

return pico;

}

然后就可以通过MutablePicoContainer的getComponentImplementation方法获得实现类,调用其push方法控制Lamp的开关,这样一来,两者之间的耦合通过PicoContainer提供的Assembler完全消除了。

Spring则通过一个XML格式的配置文件,将两者联系起来。使用时,通过ApplicationContext获得Button bean,再调用其方法实现,同样也消除了耦合关系。

3总结

IoC具有以下几个优点:

1.因为组件不需要在运行时寻找合作者,所以他们可以更简单的编写和维护。由于同 样原因,便于编写测试代码,使类的测试更容易。

2.不需要外部依赖。能在任何环境下开发和测试组件,而不需要特殊的部署环境,像 JNDI、EJB那样。并且在不同IoC容器中可方便的重用和改变。

3.整个系统更容易组装和配置。大部分业务对象不依赖于IoC容器的APIs。这使得很 容易使用遗留下来的代码,且很容易的使用对象,无论在容器内或不在容器内。

4.增加组件的复用程度,提供软件生成效率。

当然,IoC与通常的方法相比,代码不便于理解,因为组件创建是隐含的。所以轻量级的、无侵入性的IoC容器仍然有待我们去研究开发。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有