Java开发网 |
注册 |
登录 |
帮助 |
搜索 |
排行榜 |
发帖统计
|
您没有登录 |
» Java开发网 » Design Pattern & UML
打印话题 寄给朋友 订阅主题 |
作者 | IOC和Dependency Injection |
hesanxu512
发贴: 0 积分: 0 |
于 2004-10-26 00:40
介绍 其实IOC模式并不是什么新的东西,它是一种很普遍的概念(或者说结构),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模式的一种扩展的解释,下面我们从一个简单的实例开始。 考虑一个Button来控制Lamp的开和关。 拿到这个功能,我不假思索的画出了如下的类图,并写下了代码。 public class Button { private Lamp lnkLamp; public void poll() { lnkLamp.Turnon(); } } 但是马上发现这个设计的问题,Button类直接依赖于Lamp类,这个依赖关系意味着当Lamp类修改时,Button类会受到影响。此外,想重用Button类来控制类似与Lamp的另外一个对象则是不可能的。即Button控制Lamp,并且只能控制Lamp。 显然,我违反了“高层模块不应该依赖于底层模块,两者都应该依赖于抽象;抽象不应该依赖于具体实现,细节应该依赖于抽象” 这一原则(DIP原则)。 考虑到上述问题,自然地想到应该抽象出一个接口,来消除Button对Lamp的依赖,于是设计如下: 这样,我们倒置了Button对Lamp的依赖关系,使得Lamp依赖于SwitchableDevice接口,SwitchableDevice并没有依赖于Button类,任何知道如何操纵该接口的对象都可以控制Lamp。同时Button不只是可以控制Lamp,还可以控制同样实现SwitchableDevice接口的如Computer、Cell Phone等等。回头想想,这种做法好像似曾相识,拍拍脑袋,哦!这不是GoF策略(Strategy)模式吗?!正是,不经意间我就应用了设计模式(有点得意哦~~~~)。 现在再来考虑一个问题,刚才的做法虽然倒置了依赖关系,但是如果将Button作为一个应用程序来控制Lamp或者同样实现SwitchableDevice的Computer、Cell Phone等,则代码可能如下: public class Button { private SwitchableDevice lnkLamp; public Button(){ lnkLamp= new Lamp(); } … } 也就是说Button和Lamp之间仍然存在《creates》这样的依赖关系。 为了解除这种依赖关系,首先看GoF能作些什么。显然,这个地方应该用Factory模式,将对象的创建交给Factory Class来处理,这样虽然解开了Lamp组件与我们应用程序Button之间的耦合关系,但是组件的创建仍然是显式的(explicitly),在组件更改时仍需要重新编译。 另外,通过一个ServiceLocator去look up实现类也是一种解除耦合的办法,看到这儿,你不禁会想EJB不就是这么实现的嘛,U are Right! Rod Johnson 在其大作J2EE without EJB中称这种方式为Dependency Look up,但这种方式也有其弊端,比如无法脱离容器环境,以及不利于Unit test等。 “Don’t Call us, We will Call you”,这个原则启示我们应该换一个思路,不应该在应用类中创建具体对象的实例,而是应该将具体对象实例的创建插入(plug)或者说注射(inject)到应用类中,这大概是依赖注射名称的由来吧。 这种实现方式需要在应用类以及调用组件之间建立一个assembler来解除两者之间的依赖,看起来与前面的方式没有太大区别,来看一下结构: 仔细查看会发现还是有比较大的不同,依赖关系是相反的,也就是说这个过程中依然倒置了依赖关系。Lamp通过Assembler将其创建过程注射到了Button中,从而消除了两者之间的耦合,增加了灵活性。 下面我们看一下具体的实现,在PicoContainer以及Spring中有着其不同的实现,分别代表了两种类型的Dependency Injection, 即Constructor Injection 和Setter Injection。 private MutablePicoContainer configureContainer() { MutablePicoContainer pico = new DefaultPicoContainer(); pico.registerComponentImplementation(SwitchableDevice.class, Lamp.class); pico.registerComponentImplementation(Button.class); return pico; } 然后可以通过MutablePicoContainer的getComponentImplementation方法获得实现类,调用其poll方法控制Lamp的开关,这样一来,两者之间的耦合通过PicoContainer提供的Assembler完全消除了。 Spring则通过一个XML格式的配置文件,将两者联系起来,使用时,通过ApplicationContext获得Button Bean,再调用其方法实现,同样也消除了耦合关系。 后面我将会关注Spring中IOC以及AOP的实现,学习的同时会将心得以Blog的形式与大家分享。 其中类图见附件,或者http://csdn.blog.net/hesan classdigram.rar (20.78k) |
作者 | Re:IOC和Dependency Injection [Re:hesanxu512] |
Jove
CJSDN高级会员 发贴: 1228 积分: 194 |
于 2004-10-26 12:23
好文章,建议斑竹设为精华并把三张图插到原文中 另,blog的URL应该是 http://blog.csdn.net/hesan 作者好像笔误写错了 |
作者 | Re:IOC和Dependency Injection [Re:hesanxu512] |
hesanxu512
发贴: 0 积分: 0 |
于 2004-10-26 12:51
多谢jove,是笔误了 http://blog.csdn.net/hesan |
作者 | Re:IOC和Dependency Injection [Re:hesanxu512] |
hesanxu512
发贴: 0 积分: 0 |
于 2004-10-26 22:11
不错的评论! 不过我好像并没有否认DIP是更为普遍和广泛的原则,只是以DIP原则引出了Dependency Injection,基本上也说明了两者的差异之处。大家多多讨论,共同进步 |
已读帖子 新的帖子 被删除的帖子 |
Powered by Jute Powerful Forum® Version Jute 1.5.6 Ent Copyright © 2002-2021 Cjsdn Team. All Righits Reserved. 闽ICP备05005120号-1 客服电话 18559299278 客服信箱 714923@qq.com 客服QQ 714923 |