Topic: IOC和Dependency Injection

  Print this page

1.IOC和Dependency Injection Copy to clipboard
Posted by: hesanxu512
Posted on: 2004-10-26 00:40

最近网上这两个名词出现频率很高,于是抱着学习的态度读了Martin Flower的大作以及一些相关的文章,将心得与大家分享。

介绍
其实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)

2.Re:IOC和Dependency Injection [Re: hesanxu512] Copy to clipboard
Posted by: zerol
Posted on: 2004-10-26 00:57

写得不错。
hesanxu512 wrote:
考虑到上述问题,自然地想到应该抽象出一个接口,来消除Button对Lamp的依赖,于是设计如下:
some code missing?
这样,我们倒置了Button对Lamp的依赖关系,使得Lamp依赖于

3.Re:IOC和Dependency Injection [Re: hesanxu512] Copy to clipboard
Posted by: floater
Posted on: 2004-10-26 01:08

Good article.

There are more and more articles on IoC. I would like to see something now to say the weakness of IoC. Nothing is perfect, so IoC has something wrong too. Time to explore the sweetspots now, Tounge.

4.Re:IOC和Dependency Injection [Re: hesanxu512] Copy to clipboard
Posted by: Jove
Posted on: 2004-10-26 12:23

好文章,建议斑竹设为精华并把三张图插到原文中
另,blog的URL应该是 http://blog.csdn.net/hesan 作者好像笔误写错了

5.Re:IOC和Dependency Injection [Re: hesanxu512] Copy to clipboard
Posted by: hesanxu512
Posted on: 2004-10-26 12:51

多谢jove,是笔误了 http://blog.csdn.net/hesan

6.Re:IOC和Dependency Injection [Re: hesanxu512] Copy to clipboard
Posted by: think
Posted on: 2004-10-26 19:38

提点不同意见:
相对于IoC,DIP反映的是更广泛的一种设计原则,即针对抽象编程,在OO中的体现是针对Interface/Abstract BaseClass编程,在Generics中是针对Parameterized Type编程,分别对应一系列设计技巧和模式。

而IOC更关注Dependency Injection,当然前提是你要基于抽象编程,即遵从DIP原则。

另外对于IoC,我想有三点值得考虑:
1.是否需要依赖的注入,以及随之而来的依赖管理问题(配置文件)。

2.对有关IoC容器的依赖性问题,普遍采用IoC后会建立对IoC容器的依赖,这好像正是我们要消除的问题,当然这个问题不是很重要。

3.IoC容器管理的组件/模块,是开放的组件/模块,是否需考虑随之而来的系统信任/安全问题,是否需要采用一定的防御性编程来保证组件正确的状态,以及保证操作的先决条件和不定式。

7.Re:IOC和Dependency Injection [Re: hesanxu512] Copy to clipboard
Posted by: hesanxu512
Posted on: 2004-10-26 22:11

不错的评论!
不过我好像并没有否认DIP是更为普遍和广泛的原则,只是以DIP原则引出了Dependency Injection,基本上也说明了两者的差异之处。大家多多讨论,共同进步Smile

8.Re:IOC和Dependency Injection [Re: hesanxu512] Copy to clipboard
Posted by: floater
Posted on: 2004-10-27 01:26

1. yes, manage those xml files are becoming an issue as size gets larger. So we have Spring IDE eclipse plugin and JBuilder plugin from one of our moderators here. As we have more experience, the tools will get mature too. I would like to see a tool close to visio, probably built on top of jgraph, we can drag beans, write properties and the tool would write the xml files for us.

2. IoC does not necessarily introduce dependencies, we can still programatically hardcode the dependencies without IoC, this is the beauty of it, it doesn't force us to do anything, it only provides convenience for us. In one of my projects, I have to do it in both ways in order to run in different environments, it works fine.

3. I wouldn't worry about the security in the code. We have to trust the system. If the config file is compromised, probably others are compromised too. However, this is a valid concern, definitely.

One thing I like this article is that this one ties the conection between IoC and design principles(a quick chinese reference is Yan's Java and patterns). I've waiting for this in chinese for a long time. When we talk about design patterns, we should tie them to the underlying principles; when we do coding, we should follow these principles. I want to see a chinese book talking about a combination of design principles, design patterns, dependency and stability, give the logic chain of deduction, describe the consequences of various options/scenarios. Blindly copying and single-minded thinking are not quite understandable for others. For example, Yan's Java and patterns has both design principles and patterns, but doesn't put both together in a logical chain. I went to javaeye and jaction sites last night, went through some of the posts there, some folks can't get the dao pattern right, they took care of one thing and broke 5 others. That's not good. An DAO interface is setting in the middle between databases and business layers, simple and easy. Not quite, DAOs belong to the business layer, and interact with database layer. Folks should understand why and then design DAOs(It's just that DIP, and others). (Rod's book has just one line on this. When I saw that line, I laughed, he put that line somewhere and expected readers can pick it up without any further explanation, no way, I know myself I am igorant sometimes).


   Powered by Jute Powerful Forum® Version Jute 1.5.6 Ent
Copyright © 2002-2018 Cjsdn Team. All Righits Reserved. 闽ICP备05005120号
客服电话 0592-8750026    客服信箱 714923@qq.com    客服QQ 714923