二三有别 :: 梦想风暴

来源: BlogBus 原始链接: http://www.blogbus.com:80/blogbus/blog/diary.php?diaryid=141395 存档链接: https://web.archive.org/web/20050108020357id_/http://www.blogbus.com:80/blogbus/blog/diary.php?diaryid=141395


梦想风暴 一个小程序员的信口开河 <<<先作用户,后作程序员 | 主页 | 内外兼修>>> 二三有别 2004-04-15 Martin Flower在他的那篇《 Inversion of Control Containers and the Dependency Injection pattern 》中,提到了三种Injection的形式,它们分别是Interface Injection、Setter Injection和Constructor Injection,对应着IoC中谈到的type 1、type 2、type 3。除了Interface Injection由于较强的侵略性让我有些反感,其它的两种形式,我并没有看出什么不同。Spring和Pico都实现了这两种形式的Injection,只是Spring推崇Setter Injection,而Pico倾向于Constructor Injection。 一个有趣的小问题向我展示了二者之间一些差别。 问题是这样的,两个组件,如果A需要B,B也需要A。那么Setter Injection和Constructor Injection是否都能很好的解决问题呢? 如果你明白了我要说什么,就不必再浪费时间了。如果没有,继续往下看。 先来看看Setter Injection的代码。 public class A { private B b; public void setB(B b) { this.b = b; } } public class B { private A a; public void setA(A a) { this.a = a; } } 接下来是配置文件。 如果用Constructor Injection来完成,代码就变成了这样。 public class A { private B b; public A(B b) { this.b = b; } } public class B { private A a; public B(A a) { this.a = a; } } 配置文件也随之发生了变化。 显然,我们无法从字面上看出问题来,事实上,即便编译器也无法发现问题所在。那就让我们把代码运行起来。我们的测试代码也不用很复杂,读文件之后,取个bean就可以了。 InputStream is = new FileInputStream("config.xml"); BeanFactory factory = new XmlBeanFactory(is); factory.getBean("a"); 看到什么了? Setter一切正常,而Constructor疯狂的滚屏,直至堆栈溢出。 解释这个现象很容易。想想如果我们是Spring的作者,我们如何来完成这两个组件间的组合。 如果是Setter,遇到A,我们用缺省的构造函数将它构造出来,发现它需要B作为它的一个属性,因为B不存在,我们构造B。B的构造过程用的也是缺省的构造函数。发现B需要A作为它的一个属性,因为A已经存在了,就把A加进来。B完成了,回头用它把它加入到A中。 而Constructor则有所不同。遇到A,我们构造A,但A的构造参数需要B,因为B不存在,我们构造B,结果B又需要A,我们只好再构造A,而A需要B,如此反复,直至堆栈溢出。 由此可见,如果需要两个组件互相知晓,通过上面的这种Constructor Injection方式显然行不通。当然,我们可以找到其它的变通手法。 或许你会问,为什么这两个组件要互相知晓?这是一个很正常的需求,比如在MVC框架中,用户在View上的操作显然要View知道Controller在哪里才能传递过去,而Controller也需要知道View在哪才能将一些处理结果让View反映给用户。 总结一下。当两个组件需要相互知晓时,Setter Injection表现得明显要比Constructor Injection好,属于心理素质好,能够正常发挥的那种,这种精神值得中国足球队的小伙子们好好学习一下。 dreamhead 发表于 2004-04-15 21:48 引用Trackback(0) | 编辑 Comments Type2和Type3各有千秋吧,反正Spring和Pico都是同时支持的,只是有偏向而已,依实际情况而定使用了,我更喜欢Type2 dreamhead回复BlueDavy说: 我可没说type 2强于type 3,虽然我的例子不利于type 3,虽然我个人也喜欢type 2。^^ (2004-04-16 10:44) BlueDavy ( BlueDavy.blogdriver.com ) 发表于 2004-04-16 09:42 type2 的setter方法就像javaben,看起来很美de shine ( shineless.blogone.net ) 发表于 2004-04-16 13:43 我意见相反,构造器 1.更少代码 2.型运行期更安全 3.更明白我的类在用谁在干啥 iseeisee ( iseeisee.blogdriver.com ) 发表于 2004-04-17 21:42 我纳闷的是找出这样例子的理由? 这样稀有的例子还真有意思,要是在实际工作中, 我会始终简单的坚持构造器型,当遇到这样变态的情况,可以 局部的使用一下setter型: public class A { private B b; public void setB(B b) { this.b = b; } } public class B { private A a; public B(A a) { this.a = a; } } dreamhead回复iseeisee说: 这个例子不是我空想出来的,而是我根据一个同事提出的问题简化而来的。我们习惯于使用type 2,他的问题也是基于type 2提出的。正是有了这么一个机会,使我对type 2和type 3做了稍微思考。我并不鼓吹type 2或是type 3,大家完全可以根据自己的喜好来。我只是把我发现的问题分享给大家。 (2004-04-17 22:46) iseeisee ( iseeisee.blogdriver.com ) 发表于 2004-04-17 21:52 呵呵~~~~,多谢你们钻研的这末深入,我就从没想过此类问题,下次碰见堆栈溢出,可以轻松的找到原因了... iseeisee ( ) 发表于 2004-04-18 15:35 我只是说习惯于用Type 2 dreamhead回复BlueDavy说: 这叫“萝卜白菜,各有所爱”。^^ (2004-04-20 22:07) BlueDavy ( BlueDavy.blogdriver.com ) 发表于 2004-04-20 21:06 这只是spring的问题吧,没记错的话pico是可以处理循环引用的 UltraFool ( ) 发表于 2004-04-29 18:56 我记得pico的wiki是有介绍过它是怎么处理循环构造的。 fahrenheit ( ) 发表于 2004-05-17 16:37 发表评论 最近更新 走近SICP 期待2005 写在平安夜 Hello, XWork Interceptor 一年百博 精彩蛇吞象 知识的积累 单枪匹马 技术的文字 鱼与熊掌