老头子 @ 2005-06-30 12:34(转载)
背景
流行的、时髦的东西,是特别容易识别的,因为你经常会碰见它们。芙蓉姐姐是这么让我给碰上的,今天想说的IoC也是这么给我碰上的。为了给自己的blog 带来点人气,老头子决定也开始走走流行路线。所以,今天我们就来动一动虽然没有芙蓉姐姐那么火,但是也算火的IoC。
Dependency Injection
IoC,全名叫做Inversion of Control,中文名叫控制反转。我对IoC比较全面的理解,来自于Martin Fowler的这篇文章。Martin Fowler是一位很务实的设计师,他说,IoC,叫做Dependency Injection,控制注入,要更准确一些。所以他的那篇文章的题目就叫Dependency Injection模式。但是,Ioc这个名字好象更火一些,所以,我还是沿用IoC这个名字好了。
老头子的目标,是用最短的篇幅来介绍IoC,并且告诉各位IoC其实是一个很旧的概念。这是老头子的兴趣所在。
铺垫
IoC模式,处理的是对象之间的依赖关系,这是为什么Martin先生说Dependency Injection更准确的原因。所以,我们先来看看对象依赖。
比如有两个对象cA和cB。其中cA依赖于cB。通常我们会看到这样的(伪)代码:
此后,cA对象的方法,就可以直接引用cB的实例b。
这样的代码并没有啥不妥,但是很明显,cA没有对接口进行依赖,而是依赖于cB的实现,这违反了对接口而不是实现编程的原则。这样做的不好之处在于:如果 cB的实现改变了,比如改用了另一个class,cA的代码就需要做出修改。虽然新的cB实现了和老的cB相同的接口,但是替换实现导致了使用者代码的更 改,这是不好的行为。也就是说cA被cB粘住了。
咋改
首先,改成这个样子:
这样改了之后,代码好看一些,心情也舒坦一些,但是,跟没改差不多。没有解决 根本问题,cA还是要负责创建cB实例,还是得依赖cB。但是,首先iB被分出来了,为下一步改进做好了准备。
iB 被分离出来,有一个隐含的好处。就是cA代码的修改,不必担心会使用到特定的cB实现暴露出来的公用方法,因为cA对cB的使用,已经被严格地限定在iB 所声明的方法集之内。这样一来,cB的实现,只要实现一个iB接口,其它的可以海阔天空。无需担心由于其它public方法被cA不小心引用而被cA粘 住。
解耦
接口被分离出来之后,剩下的,就是怎么将 new CB() 这个代码从cA里面拿出去。这就是说,怎么样把生成cB实例的任务交给其它对象去做。
其实问题的关键,就是怎么将iB实例设置到cA.b上面去。这有两种方法:
一种就是所谓的IoC。它通过cA对象的某个方法将一个作为外部参数传入进来的iB实例赋给cA.b。根据这个方法的不同,人们把IoC又分为三种类型,虽然在我看来它们是一回事。
还有一种,叫做Service Locator。就是把生成实例的任务,委托给一个周知的对象。把对其它对象的依赖,变成对该对象的依赖。这样的话,只要该对象能稳定,问题就不大了。
IoC
先说IoC的三种方法:
1。构造子注入,也叫type 3 IoC。实际上就是用构造函数来给cA.b赋值:
2。设值方法,也叫type 2 IoC。用一个set×××方法来给cA.b赋值:
3。接口注入,也叫type 1 IoC。cA开发一个接口,让框架通过该接口来给cA.b赋值:
这三种方式,形式有所不同,实质没啥区别。总之,都是提供接口让IoC容器来 调用,并通过这些暴露的接口来完成依赖的注入。因为这个赋值方法都是被IoC容器或外部应用调用,控制权从cA转移到外部框架去了。这就是控制反转名称的 由来。
使用IoC之后,应用代码大致如下:
做得好的IoC容器,可以将 new cB() 和 new cA() 的过程全部通过配置和容器来自动完成。
Service Locator
相对来说,Service Locator更简单直观一些,它的代码形如:
其中,serviceLocator可以实现为一个Singleton,或者作为一个静态成员传递给cA。因为它是周知的对象,这不是问题。
事实上
在我看来,IoC和ServiceLocator模式,并没有太大的差别。它们都将对象的实例化交给框架或第三方去完成,而当使用者需要对象的时候,对象 已经准备好。差别只在于,对于IoC模式来说,对象赋值是由外部代码完成的,使用者处于被动地位;而对于ServiceLocator来说,对象赋值是使 用者主动请求完成的,它处于主动地位。
对于这两种模式的比较,意义并不太大。到底使用哪一个,取决于个人的偏好。老头子在这里也就不多说什么了。
旧东西
我们只要回忆一下COM框架,就会知道IoC和ServiceLocator在那里其实都是旧东西。当微软把Automation再添加到COM架构中去 的时候,我们不能不感叹,COM是个好东西。憎恨微软当然没有问题,但依我看,对于微软,不服恐怕也是不行的。
至少,产品中处处体现出先进的设计理念的微软,没有象Java社群这样喜欢鼓噪新的概念。微软能够赢得最终用户的心,那也不是没有理由的。
预告
左手剑的下一击,我想讲讲同样火的AOP是个什么东西,以及,为什么说它也是个旧东西。
背景
流行的、时髦的东西,是特别容易识别的,因为你经常会碰见它们。芙蓉姐姐是这么让我给碰上的,今天想说的IoC也是这么给我碰上的。为了给自己的blog 带来点人气,老头子决定也开始走走流行路线。所以,今天我们就来动一动虽然没有芙蓉姐姐那么火,但是也算火的IoC。
Dependency Injection
IoC,全名叫做Inversion of Control,中文名叫控制反转。我对IoC比较全面的理解,来自于Martin Fowler的这篇文章。Martin Fowler是一位很务实的设计师,他说,IoC,叫做Dependency Injection,控制注入,要更准确一些。所以他的那篇文章的题目就叫Dependency Injection模式。但是,Ioc这个名字好象更火一些,所以,我还是沿用IoC这个名字好了。
老头子的目标,是用最短的篇幅来介绍IoC,并且告诉各位IoC其实是一个很旧的概念。这是老头子的兴趣所在。
铺垫
IoC模式,处理的是对象之间的依赖关系,这是为什么Martin先生说Dependency Injection更准确的原因。所以,我们先来看看对象依赖。
比如有两个对象cA和cB。其中cA依赖于cB。通常我们会看到这样的(伪)代码:
| class cA... private cB b; public cA(){ b = new cB(); } |
此后,cA对象的方法,就可以直接引用cB的实例b。
这样的代码并没有啥不妥,但是很明显,cA没有对接口进行依赖,而是依赖于cB的实现,这违反了对接口而不是实现编程的原则。这样做的不好之处在于:如果 cB的实现改变了,比如改用了另一个class,cA的代码就需要做出修改。虽然新的cB实现了和老的cB相同的接口,但是替换实现导致了使用者代码的更 改,这是不好的行为。也就是说cA被cB粘住了。
咋改
首先,改成这个样子:
| interface iB ... class cB implements iB ... class cA ... private iB b; public cA(){ b = (iB)(new cB()); } |
这样改了之后,代码好看一些,心情也舒坦一些,但是,跟没改差不多。没有解决 根本问题,cA还是要负责创建cB实例,还是得依赖cB。但是,首先iB被分出来了,为下一步改进做好了准备。
iB 被分离出来,有一个隐含的好处。就是cA代码的修改,不必担心会使用到特定的cB实现暴露出来的公用方法,因为cA对cB的使用,已经被严格地限定在iB 所声明的方法集之内。这样一来,cB的实现,只要实现一个iB接口,其它的可以海阔天空。无需担心由于其它public方法被cA不小心引用而被cA粘 住。
解耦
接口被分离出来之后,剩下的,就是怎么将 new CB() 这个代码从cA里面拿出去。这就是说,怎么样把生成cB实例的任务交给其它对象去做。
其实问题的关键,就是怎么将iB实例设置到cA.b上面去。这有两种方法:
一种就是所谓的IoC。它通过cA对象的某个方法将一个作为外部参数传入进来的iB实例赋给cA.b。根据这个方法的不同,人们把IoC又分为三种类型,虽然在我看来它们是一回事。
还有一种,叫做Service Locator。就是把生成实例的任务,委托给一个周知的对象。把对其它对象的依赖,变成对该对象的依赖。这样的话,只要该对象能稳定,问题就不大了。
IoC
先说IoC的三种方法:
1。构造子注入,也叫type 3 IoC。实际上就是用构造函数来给cA.b赋值:
| class cA ... public cA(iB ib){ b = ib; } |
2。设值方法,也叫type 2 IoC。用一个set×××方法来给cA.b赋值:
| class cA ... public void setiB(iB ib){ b = ib; } |
3。接口注入,也叫type 1 IoC。cA开发一个接口,让框架通过该接口来给cA.b赋值:
| public interface InjectiB{ void injectiB(iB ib); } class cA implements InjectiB ... public void injectiB(iB ib){ b = ib; } |
这三种方式,形式有所不同,实质没啥区别。总之,都是提供接口让IoC容器来 调用,并通过这些暴露的接口来完成依赖的注入。因为这个赋值方法都是被IoC容器或外部应用调用,控制权从cA转移到外部框架去了。这就是控制反转名称的 由来。
使用IoC之后,应用代码大致如下:
| ...... iB b = (iB)(new cB()); cA a = new cA(b); 或者 cA a = new cA(); a.setiB(b); 或者 cA a = new cA(); a.injectiB(b); ...... |
做得好的IoC容器,可以将 new cB() 和 new cA() 的过程全部通过配置和容器来自动完成。
Service Locator
相对来说,Service Locator更简单直观一些,它的代码形如:
| class cA ... private iB b = serviceLocator.getInstance("iB"); |
其中,serviceLocator可以实现为一个Singleton,或者作为一个静态成员传递给cA。因为它是周知的对象,这不是问题。
事实上
在我看来,IoC和ServiceLocator模式,并没有太大的差别。它们都将对象的实例化交给框架或第三方去完成,而当使用者需要对象的时候,对象 已经准备好。差别只在于,对于IoC模式来说,对象赋值是由外部代码完成的,使用者处于被动地位;而对于ServiceLocator来说,对象赋值是使 用者主动请求完成的,它处于主动地位。
对于这两种模式的比较,意义并不太大。到底使用哪一个,取决于个人的偏好。老头子在这里也就不多说什么了。
旧东西
我们只要回忆一下COM框架,就会知道IoC和ServiceLocator在那里其实都是旧东西。当微软把Automation再添加到COM架构中去 的时候,我们不能不感叹,COM是个好东西。憎恨微软当然没有问题,但依我看,对于微软,不服恐怕也是不行的。
至少,产品中处处体现出先进的设计理念的微软,没有象Java社群这样喜欢鼓噪新的概念。微软能够赢得最终用户的心,那也不是没有理由的。
预告
左手剑的下一击,我想讲讲同样火的AOP是个什么东西,以及,为什么说它也是个旧东西。
Trackback地址: http://www.yculblog.com/trackback/c/746748
浙公网安备 33010602011771号