[注] 主要以Java环境为主,代码实现可带不同语言版本
提纲
- 问题的产生
- 组合模式主要内容,定义
- 组合模式的分类
- 模式UML结构
- 使用场景
- 模式实现
- 优缺点
- 注意事项
问题的产生
我们可以使用简单的对象组合成复杂的对象,而这个复杂对象有可以组合成更大的对象。我们可以把简单这些对象定义成类,然后定义一些容器类来存储这些简单对象。客户端代码必须区别对象简单对象和容器对象,而实际上大多数情况下用户认为它们是一样的。对这些类区别使用,使得程序更加复杂。递归使用的时候更麻烦,而我们如何使用递归组合,使得用户不必对这些类进行区别呢?
组合模式提供了一种解决方案,使得用户对单个对象和组合对象的使用具有一致性。
组合模式主要内容,定义
组合模式:将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。
有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以向处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦。
组合模式让你可以优化处理递归或分级数据结构。有许多关于分级数据结构的例子,使得组合模式非常有用武之地。关于分级数据结构的一个普遍性的例子是你每次使用电脑时所遇到的:文件系统。文件系统由目录和文件组成。每个目录都可以装内容。目录的内容可以是文件,也可以是目录。按照这种方式,计算机的文件系统就是以递归结构来组织的。如果你想要描述这样的数据结构,那么你可以使用组合模式Composite。
组合模式的分类
- 将管理子元素的方法定义在Composite(合成)类中 ,即所谓的__安全式__。
- 将管理子元素的方法定义在Component(元件)接口中,这样Leaf类就需要对这些方法空实现。即所谓的__透明式__。
模式UML结构
从类图中可以看出合成模式涉及3个角色:
- 抽象构件角色(Component):这是一个抽象角色,他给参加组合的对象规定一个接口,这个角色给出共有的接口及其默认行为。
- 树叶构件角色(Leaf):代表参加组合的树叶对象。一个树叶没有下级的子对象。定义出参加原始对象的行为。
- 树枝构件角色(Composite):代表参加组合的有子对象的对象,并给出树枝构件对象的行为。
他们最主要的特点区别:
- 透明方式:就是在Component里面声明所有的用来管理子对象的方法,包括add()、remove()以及getChild()方法,这样做的好处是所有的构件类都有相同的接口。在客户端看来,树叶类对象与合成类对象的区别起码在接口层次上消失了,客户端可以同等的对待所有的对象。这就是透明形式的组合模式。
- 缺点: 这个选择的缺点是不够安全的,因为树叶类对象和合成类对象在本质上是有区别的,树叶类对象不可能有一层的对象,因此add()、remove()以及getChild()方法没有意义,但是在编译时期不会出错,而只会在运行时期才会出错。
- 安全方式:是在Composite类里面声明所有的用来管理子类对象的方法。这样的做法是安全的做法,因为树叶类型的对象根本就没有管理子对象的方法。因此,如果客户端对树叶对象使用这些方法时会在编译时期出错。编译不通过,就不会出现运行时出错。
- 缺点: 这个选择的缺点是不够透明,因为树叶类和合成类将具有不同的接口。
使用场景
- 你想表示对象的部分-整体层次结构
- 你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
模式实现
场景: 雇员,员工,和leader,之间的关系。
我们这里具的例子属于安全式组合,给出的组合模式抽象角色里,并没有管理子节点的方法,而是在树枝构建角色,这种模式使得叶子构建角色和树枝构建角色有区分,客户端要分别对待树叶构建角色和树枝构建角色,好处是客户端对叶子节点不会调用管理的方法,当调用时,在编译时就会报错. 透明式大同小异,就不举例了.
UML图如下:
员工和领导的统一接口
普通员工类
领导类
测试类
上面员工关系的的树形结构如下:
优缺点
- 优点
- 缺点
- 使用合成模式后,控制树枝构件的类型就不太容易。
- 用继承的方法增加新的行为很困难。
注意事项
- 透明式组合是将管理子元素的方法定义在Component(元件)接口中
- 安全式组合是将管理子元素的方法定义在Composite(合成)类中
- 如果组合模式中出现大量相似的组件对象,就可以考虑使用享元模式来帮助组件缓存