读书人

研磨设计形式 之 组合模式(Composite)

发布时间: 2012-10-29 10:03:53 作者: rapoo

研磨设计模式 之 组合模式(Composite) 3——跟着cc学设计系列

15.3? 模式讲解15.3.1? 认识组合模式

(1)组合模式的目的

组合模式的目的是:让客户端不再区分操作的是组合对象还是叶子对象,而是以一个统一的方式来操作。

实现这个目标的关键之处,是设计一个抽象的组件类,让它可以代表组合对象和叶子对象。这样一来,客户端就不用区分到底是组合对象还是叶子对象了,只需要全部当成组件对象进行统一的操作就可以了。

(2)对象树

通常,组合模式会组合出树形结构来,组成这个树形结构所使用的多个组件对象,就自然的形成了对象树。

这也意味着凡是可以使用对象树来描述或操作的功能,都可以考虑使用组合模式,比如读取XML文件,或是对语句进行语法解析等。

(3)组合模式中的递归

组合模式中的递归,指的是对象递归组合,不是常说的递归算法。通常我们谈的递归算法,是指“一个方法会调用方法自己”这样的算法,是从功能上来讲的,比如那个经典的求阶乘的例子,示例如下:

public class RecursiveTest {

??? /**

??? ?* 示意递归算法,求阶乘。这里只是简单的实现,只能实现求数值较小的阶乘,

??? ?* 对于数据比较大的阶乘,比如求100的阶乘应该采用java.math.BigDecimal

??? ?* 或是java.math.BigInteger

??? ?* @param a 求阶乘的数值

??? ?* @return 该数值的阶乘值

??? ?*/

??? public int recursive(int a){

?????? if(a==1){

?????????? return 1;

?????? }?????

?????? return a * recursive(a-1);

??? }??

??? public static void main(String[] args) {

?????? RecursiveTest test = new RecursiveTest();

?????? int result = test.recursive(5);

?????? System.out.println("5的阶乘="+result);

??? }

}

而这里的组合模式中的递归,是对象本身的递归,是对象的组合方式,是从设计上来讲的,在设计上称作递归关联,是对象关联关系的一种,如果用UML来表示对象的递归关联的话,一对一的递归关联如图15.4所示,而一对多的递归关联如图15.5所示:

? ? ? ? ? ? ? ? ? ? ? ? ? ??研磨设计形式 之 组合模式(Composite) 3——跟着cc学设计系列???????????????

图15.4? 一对一递归关联结构示意图???

研磨设计形式 之 组合模式(Composite) 3——跟着cc学设计系列

图15.5? 一对多递归关联结构示意图

另外组合对象还有一个特点,就是理论上没有层次限制,组合对象A包含组合对象B,组合对象B又包含组合对象C……,这样下去是没有尽头的。因此在实现的时候,一个必然的选择就是递归实现。

(4)Component中是否应该实现一个Component列表

?????? 大多数情况下,一个Composite对象会持有子节点的集合。有些朋友可能就会想,那么能不能把这个子节点集合定义到Component中去呢?因为在Component中还声明了一些操作子节点的方法,这样一来,大部分的工作就可以在Component中完成了。

?????? 事实上,这种方法是不太好的,因为在父类来存放子类的实例对象,对于Composite节点来说没有什么,它本来就需要存放子节点,但是对于叶子节点来说,就会导致空间的浪费,因为叶节点本身不需要子节点。

?????? 因此只有当组合结构中叶子对象数目较少的时候,才值得使用这种方法。

(5)最大化Component定义

?????? 前面讲到了组合模式的目的是:让客户端不再区分操作的是组合对象还是叶子对象,而是以一种统一的方式来操作。

由于要统一两种对象的操作,所以Component里面的方法也主要是两种对象对外方法的和,换句话说,有点大杂烩的意思,组件里面既有叶子对象需要的方法,也有组合对象需要的方法。

???????其实这种实现是与类的设计原则相冲突的,类的设计有这样的原则:一个父类应该只定义那些对它的子类有意义的操作。但是看看上面的实现就知道,Component中的有些方法对于叶子对象是没有意义的。那么怎么解决这一冲突呢?

?????? 常见的做法是在Component里面为对某些子对象没有意义的方法,提供默认的实现,或是默认抛出不支持该功能的例外。这样一来,如果子对象需要这个功能,那就覆盖实现它,如果不需要,那就不用管了,使用父类的默认实现就可以了。

?????? 从另一个层面来说,如果把叶子对象看成是一个特殊的Composite对象,也就是没有子节点的组合对象而已。这样看来,对于Component而言,子对象就全部看作是组合对象,因此定义的所有方法都是有意义的了。

(6)子部件排序

在某些应用中,使用组合模式的时候,需要按照一定的顺序来使用子组件对象,比如进行语法分析的时候,使用组合模式构建的抽象语法树,在解析执行的时候,是需要按照顺序来执行的。

对于这样的功能,需要在设计的时候,就要把组件对象的索引考虑进去,并仔细的设计对子节点的访问和管理接口,通常的方式是需要按照顺序来存储,这样在获取的时候就可以按照顺序得到了。可以考虑结合Iterator模式来实现按照顺序的访问组件对象。??????

15.3.2? 安全性和透明性

?????? 根据前面的讲述,在组合模式中,把组件对象分成了两种,一种是可以包含子组件的Composite对象,一种是不能包含其它组件对象的叶子对象。

?????? Composite对象就像是一个容器,可以包含其它的Composite对象或叶子对象。当然有了容器,就要能对这个容器进行维护,需要向里面添加对象,并能够从容器里面获取对象,还有能从容器中删除对象,也就是说需要管理子组件对象。

??? 这就产生了一个很重要的问题:到底在组合模式的类层次结构中,在哪一些类里面定义这些管理子组件的操作,到底应该在Component中声明这些操作,还是在Composite中声明这些操作?

??? 这就需要仔细思考,在不同的实现中,进行安全性和透明性的权衡选择。

读书人网 >操作系统

热点推荐