论面向组合子程序设计方法 之二 失乐园

前面我们讲到一些OO和CO的区别。肯定有人不以为然。什么CO?OO包罗万象,你所谓的CO也不过是OO的一个分支而已。

是啊,易经也包罗万象,也许太极图或者伏羲六十四卦早就可以描述蒸汽机,喷气战斗机,计算机,烤火鸡,等等,更何况什么OO,CO呢?

其实,如果把OO当作一个大箩筐,什么都往里装,自然,我这里要说的也不会跳出它的藩篱。毕竟我这CO还是用java实现的呢,归根结底,不是还要用class, interface之类的东西乱搞?

而我所谈的OO,和老庄不同,不是那么一个宽泛的概念。实际上,我说的OO包括两个阶段。

第一个阶段: 1。认识到数据和行为的统一性。用类来封装数据,隐藏数据,并且对行为建模。这种更高级的ADT应该是OO的最基本形态。 2。用类继承的方式来渐进式地描述现实生活中的概念。对概念之间的差别用子类覆盖父类某一个方法的途径来表达。

在这个阶段,传统的“封装,继承,多态”就已经被提了出来。

这种看上去很美的非常符合人的直觉的方法被人们很快地接受了。

毕竟,Person, Vehicle, Animal等等做成类是多么直白,vehicle.start(), person.work(), animal.eat()看上去是多么简单。 人们象吃了善恶果一样发现眼睛似乎一下子明亮了,世界看上去竟然那么分明。靠,老子怎么没早知道有这个果子?还有没有?再给两个!

但是,真正应用到变化莫测的现实世界,人们逐渐发现这个方法不象它看上去那么简单。多重继承的困扰,父子类之间的强烈的耦合都造成了巨大的麻烦。 我们经常发现,或者父类提供了很多东西,可我根本不需要。或者我想要某个东西,可是父类偏偏没有设想到这个可能,没有提供,而父类自作聪明预先设计好的某个设施,不但没用,反而给子类造成麻烦。

于是,四人帮们振臂高呼:不要以为OO就是随意设计你自己的类。你要遵守一定的模式!

于是,二十几片树叶被精心编织成一块遮羞布遮在OO的私处上,我们真的知道了“善恶”。一个完善的OO道德体系被圣人们建立了起来。

第二个阶段: 模式真的解放了我们吗?还是我们更糊涂了?曾几何时,我们开始痛恨singleton,鄙视template method,害怕visitor。 是时候该反思一下了。我们真的需要“继承”吗?“重载”真的很好吗?

理论界早就在研究subtype和subclass的区别,我们发现,似乎有必要区分这两个概念,subtype不见得就必须subclass,甚至,连subclass也不一定就非得subtype。

c++阵营里出现了gp的声音。大有一举将OO这个老朽打翻在地的气势。模板!到处是模板。我们用模板组合模板,代替了传统的继承,一些人甚至做出了一些相当技巧性的东西。

java跳了出来,坚定地把subtype和subclass分开。于是,我们有了interface。

所谓interface,理论上就是一个type。它和class不同,type/interface只定义方法的签名,但是不包含具体数据和实现,它只表示:给你这个类型的对象后,你能用它来做什么。它不包含:这个类型的对象具体是怎么做某一件事的。

这样,class代表实现,interface代表规范。“implements"代表subtype,“extends"是subclass同时也是subtype。 subtype和subclass被初步地分开了。我们不再仅仅有几片树叶来遮挡私出,现在我们还有了bra。

“继承”的作用被弱化,甚至被反对。要求用接口组合来代替继承的呼声日高。确实仍然有用继承和template method比较方便的场合,但是这不再对系统的整体架构产生影响。“继承”更多地被用作一种局部的实现手段,它影响的不再是整个系统骨架,而是三两个本 身就紧密耦合的类。

interface的出现在OO内部是一个决定性的事件——如果不是一个革命的话。

它进一步完善了OO的“职责分派”体系。功能的“消费者”和“提供者”可以被明确地区分。

消费者只看见接口,而看不见类,这样,留下了一块切换不同“提供者”的空间。

OO的整体哲学甚至被上升了一个高度:我们不再执著于用树形结构,渐进式地描述世界,因为我们认识到这不大可行,世界比我们想象的要复杂得多。

OO在这个阶段被重新诠释为:一个责任分工体系。 遇到一个问题,我们通过分析需求,把它分解成互相协作的子模块。每个子模块有精确定义的职责。子模块之间通过精心设计的接口来互相通信,在保证最小耦合的基础上实现了分工合作。

所谓ioc就是这样的思想的一种表达。子模块需要一个外部提供的功能时,不是直接去找到某个具体实现模块,而是通过接口把协议公开出去。这和上帝 他老人家的设计有点形似了:夏娃需要生儿子,但是虽然暂时旁边只有一个adam gg,但是夏娃的身体构造却不是仅仅为adam设计的,上帝他老人家给夏娃的是一个跟任何男人都能生儿子的通用接口。 女人这个模块的职责被定义为:跟男人(们)生孩子。 男人这个模块的职责被定义为:跟女人(们)生孩子。

在这种思路成为系统的主导思想的时候,子模块的组装成为了一个问题。谁负责把鸡犬相闻(只看见接口),老死不相往来(看不见类)的子模块最终组合在一起呢?当只有adam夏娃的时候,上帝一努嘴,俩人就睡一起了。但是当天下呼呼嚷嚷都是男人女人的时候,这就不够效率了。 于是,月下老人就开了一个ioc容器,来负责把这些用ioc方式设计的组件们用一种最经济的方式拉到一起。

但是,有一点从OO的前驱PO那里开始到现在都没有变化,那就是,它们始终都是自顶向下的逐渐细化求精的方法论。

还有一个不太为人所关注的共同点:他们,PO, OO,都是命令式的。它们都同样以一个状态机的状态变迁为着眼点来运作系统。

回头看看还在不甘心地吵嚷不休的c++的gp,它真是一个单独的方法论吗? 它难道不是也通过自顶向下的方法逐步求精?concept-model难道不就是interface-class的另一种表达形式?它难道不是也 基于状态变迁的命令式系统?(meta-programming不是,但是此处我们暂时不考虑这种生成式编程,因为它在gp里面并不算主流) 它难道不是仍然用类来封装数据,用方法来实现行为?

确实,模板因为concept不是一个强制性的类型,任何类,只要具有某个concept要求的函数签名(实际上甚至要求比这个还要松的),就可以被当作实现这个concept的一个model。如此节省了我们写adapter的工作。 另外,模板组合也比基于运行时多态,以来gc的接口组合效率高。 但是,这些细枝末节并不能qualify一个单独的方法论。

所以,我们认为,gp和programming against interface在方法论上殊途同归,在实现层面上则各有利弊。

好了,絮絮叨叨这么多,就是要给我所谈到和要进行比较的“oo”画一个圈子,不能说是“权威定义”,但是,这就是我要谈的那个“OO”。

下一节开始,我们就可以正式开始看看怎么用CO来解决我前面提出的那个例子。

与此同时,有心人也可以想想用OO(inheritance-oriented也好,interface-oriented也好)这种自顶向下的方式怎么实现这个需求繁杂,甚至有些条款还不确定的例子。

我自己是想不出太好的方法。一大堆if-else我不喜欢,父子类之间混乱的override这种乱伦行为我也有力不从心之感。