动机
程序经常需要操作树形数据结构,而且需要同等对待分支和叶子节点。我们来思考下一个文件管理系统。文件管理系统是由分支(文件夹)和叶子节点(文件)组成的树形结构。注意,一个文件夹通常包含一个或多个文件或文件夹,这样的是一个复杂对象;相对地,文件是简单对象。我们还注意到由于文件和文件夹的很多操作和属性都是一样的,比如移动、复制、列出文件或文件夹的属性(如名称和大小);那么要是我们定义一个文件系统资源接口来把文件和文件夹对象统一对待,操作上就会方便很多。
目的
- 此模式的目的就是为了将对象组合成树形结构,以此来表示部分-整体的层级
- 组合使得客户端能一致地对待单个对象和对象组合
实现
示例代码
- Component - 组件接口是叶子节点和复合的抽象。它定义了复合内对象必须实现的接口。比如说文件系统资源为文件和文件夹定义了移动、复制、重命名和获取大小的方法。
- Leaf - 叶子节点是那些没有子元素的对象。它们实现了 Component 接口定义的服务。比如一个文件对象实现了移动、复制、重命名等 Component 接口里的相关行为
- Composite 复合对象存储子元素外还实现了 Component 中定义的行为。复合通过将行为委托给子组件的方式来实现 Component 中的方法。此外,复合对象还提供了新增、移除和获取子组件的方法。
- Client 客户端通过 Component 接口操作处于层级中的对象
如果客户端需要对所有节点执行操作,而且不管节点到底是分支还是叶子节点。那么客户端只需要通过 component 接口持有所需节点的引用,然后通过这个接口机进行处理就行了。
应用场景 & 例子
组合模式适用于存在部分-整体的层次结构,并且客户端需要一致地对对象进行处理而不管对象是分支还是叶子节点。
例子 - 图形编辑器
在图形编辑器中,图形可以是基本的也可以是复杂的。举例来说,直线是简单的图形,矩形是由四条直线组成的复杂图形。由于各种图形有许多一样的操作,如渲染到屏幕上;而且图形遵循着部分-整体的层次结构,组合模式就能用来让程序一致地处理所有图形的行为。
例子中各角色定义如下:
- Shape (Component) - 图形是直线、矩形(叶子节点)或其他复杂图形(复合对象)的抽象
- Line,Rectangle(Leafs) - 无子元素的对象。它们实现接口 Shape 中描述的行为
- ComplexShape (Composite) - 持有子 Shape 的复合对象,此外还实现了接口 Shape 中定义的方法
-
GraphicEditor (Client) - 图形编辑器操作层次结构中的图形 Shape
另一种实现: 在前面的例子中,我们没有通过接口 Shape 来组合对象,而是在复合对象中特殊处理。为了避免这种情况,并进一步加强操作的一致性,我们将组件的新增、移除和获取操作上升到接口 Shape 中定义,uml 如下:
总结
- 组合模式定义由简单对象和复合对象组成的类层次。简单对象可组合成更复杂的复合对象,相应地复合对象可以继续组成更复杂的复合对象
- 客户端可一致地对待简单对象和复合对象,使用 component 接口使得客户端代码保持简洁
- 客户端通过统一的 component 接口处理组件,所以可以添加新的组件会比较容易,不用修改客户端代码
相关模式
Decorator Patterm 装饰者模式经常和组合模式一起使用。当装饰者和组合模式一起使用的时候,它们将有一个共同的父类。然后装饰者对象需要支持组合模式的组件接口 Component ,如 Add, Remove 和 GetChild。