可以这么说:只有你需要开发框架软件时才需要Ioc,如果你的框架已经基于一个很重的大框架(如Struts+EJB),那么使用Pico要给你很多灵活性。
在一般final之类的应用系统中不必用Ioc等东西,直接new最简单,直接final性能最优,一般C++有相当造诣的人都喜欢这么做,就象以前“程序员”杂志上那个台湾大师写了一个关于序列化的文章,分析细化到字节,我敢说,用他这样思维来搞Java,死定了。
搞Java,思维要向上走。
可以这么说:只有你需要开发框架软件时才需要Ioc,如果你的框架已经基于一个很重的大框架(如Struts+EJB),那么使用Pico要给你很多灵活性。
在一般final之类的应用系统中不必用Ioc等东西,直接new最简单,直接final性能最优,一般C++有相当造诣的人都喜欢这么做,就象以前“程序员”杂志上那个台湾大师写了一个关于序列化的文章,分析细化到字节,我敢说,用他这样思维来搞Java,死定了。
搞Java,思维要向上走。
另外,IOC已经被Martin大叔给命名为依赖注射,那么类A与类B有依赖关系的代码就是:
class AImpl{ B b = new BImpl(); }
如果B实现换成BImpl1那么AImpl代码就要改动,复用性和扩展性都差。这是非常简单的例子了。不过多数人现在书写的代码中充斥着这样的代码。
我不熟悉PICO,但我在使用SPRING,它的bean and application context就象一个容器一样,不仅仅管理业务层对象之间那种临时的依赖关系,而且数据层等也一样容易被管理。方式就是加载XML文件。如我上篇所提到的,这种依赖关系的代码一定要剔除。有些公司用数据库建表的方式管理,SPRING给你一个这样的抽象类,使这一过程非常简单。
这样做的结果便是代码中只有接口在互相交互。具体的实现都在XML文件中指定。不仅仅如此,上下文和bean的加载就是一个组合的模式。这样是控制器和业务层,数据层的组合在更高一级组合。如果说原来写“管道”代码,现在就是在建楼房。楼房要一层一层建,建好后在去粉刷,装修,这些就会用AOP了。不仅仅日志管理,事务管理也是一个典型的应用。spring已经提供了和EJB一样的声明式事务管理。欠缺的可能就是象JBUILDER的选取和自动生成吧。
好象我的例子又造成误会了。
我的意思是,pico完全依赖共有构造函数来完成注射。这不够灵活。注射实现除了用setter, 用构造函数,也可以用工厂方法的。客户完全可能选择隐藏构造函数而使用工厂方法来注射。
这和用singleton, 工厂好不好是两码事。客户应该有设计的自由。pico作为一个号称很灵活的库,不应该对客户的设计提出太苛刻的需求。宣称“过度使用工厂不好”而不支持工厂和singleton怎么看都是巧言令色。实际上,工厂方法除了在reflection里面没有一个固定的命名标准,在传统的编程模型里,明显比构造函数有更好的灵活性。
另外,从c.instance()是看不出来是否单态的。实现者完全有自由让它是单态或者直接调用私有的构造函数。
>
> 另外,IOC已经被Martin大叔给命名为依赖注射,那么类A与类
> 有依赖关系的代码就是:
> class AImpl{ B b = new BImpl(); }
> 如果B实现换成BImpl1那么AImpl代码就要改动,复用性和扩展
> 远疾睢U馐欠浅<虻サ睦恿恕2还嗍讼衷谑樾吹拇胫
> 充斥着这样的代码。
>
没有人怀疑ioc的作用。我倒是有点奇怪这么个基本的面向接口的常见做法也需要这么长篇幅来解释。
这里讨论的是用pico方式实现ioc的作用。
> 我不熟悉PICO,但我在使用SPRING,它的bean and
> application
> context就象一个容器一样,不仅仅管理业务层对象之间那种?> 时的依赖关系,而且数据层等也一样容易被管理。方式就是加
> XML文件。如我上篇所提到的,这种依赖关系的代码一定要剔
> S行┕居檬菘饨ū淼姆绞焦芾恚SPRING给你一个这样?> 抽象类,使这一过程非常简单。
>
我不喜欢用setter来注射实现,这一方面有非法状态的问题(假如我忘了注射实现怎么办?),另一方面,它强迫我不能使用immutable的设计,这点很让人反感。
> 这样做的结果便是代码中只有接口在互相交互。具体的实现都在XML文件> 中指定。不仅仅如此,上下文和bean的加载就是一个组合的模式。这样> 是控制器和业务层,数据层的组合在更高一级组合。如果说原来写“管> 道”代码,现在就是在建楼房。楼房要一层一层建,建好后在去粉刷,> 装修,这些就会用AOP了。不仅仅日志管理,事务管理也是一个典型的应> 用。spring已经提供了和EJB一样的声明式事务管理。欠缺的可能就是象> JBUILDER的选取和自动生成吧。
接口互相交互是面向接口编程的理想状态。但是“具体的实现都在xml文件中”就不是面向接口的精神了。
其实,具体实现到底在哪里给出,完全是程序员自己灵活决定的,
从ptoprties,从gui,从xml文件,从jndi,从pico,从abstract factory,从rmi接口,所有的方式都有它们存在的合理性和必要性。
用xml配置这样一种方法来一统天下,思想显得太僵化了。面向接口的好处就是实现灵活,没有一定之规。
总而言之,一个好的库,或者框架,应该是尽量和客户代码orthogonal的,也就是说,它不对客户代码和设计方法做任何限制,任何强迫。
一个好的习惯是,用接口描述库和客户代码之间的协议。
反例如:
1。要求客户继承某一个鸡肋。这就剥夺了客户类继承其它鸡肋的权力。
2。要求客户类的构造函数共有。这就剥夺了客户用工厂方法的自由。
3。要求客户类提供setter,这就剥夺了客户类immutable的自由。而且强迫客户类增加可能本来不需要的状态(如uninitialized)
pico在我看来就有2这个毛病。当然,从pico的体系上来看,支持工厂方法也不是难事。只要pico的设计者认为值得为了这个灵活性在pico的接口里增加方法。(我认为是值得的)
其实,不论pico是好是坏,我都建议在代码不需要依赖于它的时候还是把它屏蔽掉最好。依赖越少,软件就越容易维护,越健壮。
比如你文章中举的例子,它可以用来描述pico的一些用法。
但是,如果实际应用中,我们本来只需要:
new A(new B(), new C());
而非要使用pico,搞什么
pico.registerComponent(A.class);
pico.registerComponent(B.class);
pico.registerComponent(C.class);
pico.getComponentInstance(A.class);
我看那纯粹是自找麻烦。
pico解决的是对象组装,是所有模块都做好之后的搭积木的工作。模块内部如果需要构造对象,还是用ioc,从外面注射进来一个抽象工厂对象更灵活。
如果在模块内部到处使用pico,这和使用new,或者使用jndi也还是一样,换汤不换药,我想这不是一个好的设计。
等你开始真正做一个很灵活的不依赖具体实现的框架,或者说,你设计开发软件时,开始最大限度追求重用性和伸缩性了,我想你会明白那些“自找麻烦”的事。
如果有人对“自找麻烦”的原因感兴趣,唯一释惑的方式就是:解读他人源码,目前我推荐:Portal门户开源Exo:
我自己也做过一些框架的,只不过,我很少花心思去考虑xml配置之类的事情,我认为那已经是系统的外围附件了。我一般会尽量让我的框架不依赖任何一种特定的编程模式,也会避免让它依赖ejb啦,pico啦等类库。如果框架设计得好,理想状况是客户尽可以用任何他们喜欢的类库或编程模型,但是框架不会强迫客户使用这个类库,不使用那个类库。
在我看来,依赖pico,jndi等api,已经是系统不灵活的征兆了。
我用什么东西都是要看出它的好处和坏处,不会因为大家都说它好或者某些著名的project在用它就跟着别人跑。
banq,我估计我们的分歧根源还是在于对问题域的认识不一致。你可能是看到了一些应用领域里面使用pico确实造成了方便。而我却没有看到。
本来希望你能够举出一些具体的例证来说服我,可惜,看来,你已经失去了开始时对讨论这个话题的兴趣了。
好了,不谈了,还是让实践说话吧。
但是,不知道你所说的“工厂”是指什么?
我前面说工厂灵活,是因为抽象工厂可以描述任何一种对象创建机制,这包括xml配置文件或者pico。
也就是说,定义一个抽象工厂,你可以有任意的扩展,而如果硬编码进去xml或者pico,那就被绑定在一种实现上了。
比如:
|
至于我说的不支持工厂方法,是指pico的api,我没看过spring,不能乱说,但是pico要求每个component都是用public constructor来构造,这就不够灵活。评什么我用了pico就不能用singleton,不能用工厂方法?
但是反过来,如果对象创建直接依赖于xml配置,那么就无法实现一些更灵活,或者更变态的需求。
我本来倒是试图把讨论局限在明显的例子上,比如pico如何处理实例创建使用工厂方法和singleton的情况之类,但是怎么感觉咱们似乎对更抽象的辩论更有兴趣?
banq经验丰富,所以喜欢把讨论大而化之到“真正灵活的框架”这类形而上的概念上,而对等而下之的具体列出灵活性和不灵活性这种体力活不感兴趣。而据我的经验,讨论一旦达到这种“谈理想,谈人生”的高度,就肯定不可能有结果的。^_^。
而我和sharon似乎都被对方的思路弄晕了。我敢说,我们俩谁也没有真正明白对方到底要说什么。
好啦,兄弟们,打住吧。很高兴认识banq和sharon,以后各位谁要是有兴趣对某一个具体设计实例或者问题讨论,别忘了叫上小弟,同去,同去,于是一同去!^_^
我没具体的用过pico(只看过它的例子和称不上是文档的文档),所以你说的那些基本上是鸡同鸭讲了。
至于spring,我觉得他的beanfactory只是类型化弱一点的工厂,解决的主要是singleton问题和依赖关系可配置性(对于web应用而言,其实jakarta中几个子项目的组合也能做到这一点,但没它做的完善)。
不过,我估计我是还没碰到你说的那种"变态的"灵活性。至少在我看来,这个已经够灵活了。
面向过程是完全从代码的角度对系统进行划分的思想。而面向对象则是从(数据+对数据的操作)的角度对系统进行划分的思想。
在已有的划分已经完成的情况下,我们会发现存在很多的代码冗余,走会原来的面向过程的老路又不现实,因此出现了AOP。
我也不了解spring的。只是在pico的网站看到了一点和spring的比较,其中说道spring依赖于setter注射。
这真是瞎子摸象了。^_^
关于灵活性,其实,没有系统有无限的灵活性,(除非什么也不做,光默念“无招胜有招”)。所谓灵活性都是在和简单性的权衡中来说的。过于简单,不能对需求的变化做任何应对自然不好,但是为了最大的灵活性而引入过多的复杂性也一样走了极端。这second system syndrome其实到处可见。
所以说,所谓过犹不及,“够用”两字确实是相当重要啊。
我说到pico的不灵活的地方,也不是说它就不成了。还是看够用不够用。而不知道它所面向的问题域的具体情况,就很难说它够用不够用。
前面我说如果本来只需要new C(new A(), new B())而非要去pico是自找麻烦,也是本着一个“够用”来说的。可惜我表达得不好,让人觉得我好像是在批评使用pico的人一样。冤枉啊!
>所谓灵活性都是在和简单性的权衡中来说的。过于简单,不能对需求的变>化做任何应对自然不好,但是为了最大的灵活性而引入过多的复杂性也一>样走了极端。这second system syndrome其实到处可见。
>所以说,所谓过犹不及,“够用”两字确实是相当重要啊。
灵活性和简单性是不矛盾的,认为矛盾的人是因为他们还没有抓住问题的本质,问题本质抓住,就非常简单,也就具备相当的灵活性。
做软件我觉得千万不能有“够用”的思想,而是要有“变化”的思想,经常问自己,我这样做的前提限制条件是什么,如果需求变化了,正好击中我这样做的前提限制条件怎么办。
程序员一定要有“计划没有变化快”的思想,树立需求第一,没有不变的需求的思想,我认为这是作为一个程序员的基本素质,如果我招聘程序员,我不会出试卷考他有多少技能,这些以后都可以学会,我会考他的做程序的思想,这真是世界观的问题,世界观错误,基本方向就不对,再高技能也是空的。
所以,从这个角度来看,现在软件技术为什么强调复用,为什么要有框架?为什么要面向接口?这些好像是“自找麻烦”?
这些“自找麻烦”概率推出正是帮助程序员应付不断变化的需求,因为需求变化快,程序员不可能来一个需求就重新建一个系统或程序,如果我们试图在这些程序找出不变的东西,固化抽象成框架之类东西,那么需求变化时,我们只要在这个框架下根据具体变化的新需求开发少量的新的程序就可以了。这不是减轻程序员工作量?换言之,这样能程序员更快跟随需求变化。
上述道理我已经说了很多,也有人反驳过,不过我现在的实践确实是这么做的,如我的JdonSD框架已经在OA、电子商务系统应用,即将应用到一个大型的物流系统,让那些还在怀疑重用性可能有多大的人,能不能重用的程序员随着大浪淘汰去吧。
banq,我觉得你之所以觉得灵活性和简单性不矛盾,是因为你还没有真正去思考什么是最大的灵活性。就象这个pico一样,我提出的几个不灵活性,如不能用singleton什么的,你是不是也认为那无所谓,因为你反正用不到?
其实就说pico,要是真要无限地追求灵活性,那么,下面的要求它应该做到的:
1。支持用工厂方法构造对象。
2。支持用抽象工厂构造对象。
3。支持对特定的类的构造在特定的构造位置选取特定的实现类注射。比如
new A(new B1(), new B2())
new C(new A(new B1()))
这应该可以通过xml的配置来做到。
4。支持从jndi locate对象而不是调用构造函数。
5。在构造new A(new B1())失败的时候,通知客户的listener回调函数。
......
如果愿意,这个变态的列表还可以列出很多。
我觉得你的思想和我几年前比较象,就是比较理想化地认为面向接口可以提供无限的灵活性,几乎就是银弹。
我记得在哪里看到一个哲人的一句话,现在越咀嚼越有道理:软件设计就是根据具体情况作出合理的假设。
假设做的越多,系统灵活性就越差。那么,为什么还要做假设呢?就是因为有些合理的假设可以大大地简化实现的难度。
所以,系统设计的难点在于权衡每一个假设是否值得。或者说,每一个灵活性是否值得它的复杂性。
有些灵活性,就象你所见那样,如ioc,如一些简单的factory等pattern,还有很多对接口编程的技术,对复杂性的影响很小,如果系统nontrivial的话,本来需要付出1元钱的,用了这些pattern,不过付出1块零一分,而得到的灵活性的好处却是很大的。这些灵活性就往往应该保持。
软件设计和很多其它的事情一样,不要走极端。不是说,怀疑某些解决方案的复杂度,就等于只要眼前利益,不管扩展性。也不是说,关注系统的可重用性,扩展性,就可以不顾实际地复杂化问题。
关键的问题还是在于一个权衡。一个好的设计者,和一个坏的设计者的区别也就在这个权衡的能力和经验。
敏捷开发的提出,refactor的提出,也就是给出了一个大致可以遵循的权衡标准。
最后,今天正好看到了一篇文章,文笔很有趣,看后也对自己有所警醒。贴出来大家一起欣赏一下:
http://www.javaresearch.org/article/showarticle.jsp?column=31&thread=6885
http://www.javaresearch.org/article/showarticle.jsp?column=31&thread=6886