组合模式
文章目录
组合模式用于将若干对象组合成树形结构
,以表示"部分-整体"的层次结构。
- 使客户端对单个对象和组合对象的使用具有一致性。
- 只用于特定的树形组织场景,如文件管理、组织管理,能简化管理使代码变得简洁。
如果应用的核心模型能用树状结构表示,在应用中使用组合模式才有价值。
背景
你有两类对象:产品和盒子 。
- 盒子中可以包含多个产品或者几个较小的盒子 。
- 小盒子中同样可以包含一些产品或更小的盒子,以此类推。
假设在这些类的基础上开发一个定购系统。订单中可以包含无包装的简单产品,也可以包含装满产品的盒子以及其他盒子。此时如何计算每张订单的总价格呢?
可以尝试直接计算:打开所有盒子,找到每件产品,然后计算总价。
- 这在真实世界中或许可行,但在程序中,不能简单地使用循环语句来完成该工作。
- 因为必须事先知道所有产品和盒子的类别,所有盒子的嵌套层数以及其他繁杂的细节信息才能开始循环。
- 因此,直接计算极不方便,甚至完全不可行。
解决方案
组合模式建议使用一个通用接口来与产品和盒子进行交互,并且在该接口中声明一个计算总价的方法。
- 对于一个产品,该方法直接返回其价格;
- 对于一个盒子,该方法遍历盒子中的所有项目,询问每个项目的价格,然后返回该盒子的总价格。
- 如果其中某个项目是小一号的盒子,那么也会遍历其中的所有项目,以此类推,直到计算出所有内部组成部分的价格。
- 你甚至可以在盒子的最终价格中增加额外费用,作为盒子的包装费用。
该方式的最大优点在于无需了解构成树状结构的对象的具体类,也无需了解对象是简单的产品还是复杂的盒子。
只需调用通用接口以相同的方式对其进行处理即可。调用该方法后,对象会将请求沿着树结构传递下去。
使用场景
- 如果需要实现树状对象结构,可以使用组合模式。
- 组合模式提供了两种共享公共接口的基本元素类型:简单叶节点和复杂容器。容器中可以包含叶节点和其他容器,因此可以构建树状嵌套递归对象结构。
- 如果希望客户端代码以相同方式处理简单和复杂元素,可以使用该模式。
- 组合模式中定义的所有元素共用同一个接口。在这一接口的帮助下,客户端不必在意其所使用的对象的具体类。
实现步骤
- 确保应用的核心模型能够以树状结构表示。尝试将其分解为简单元素和容器。容器必须能够同时包含简单元素和其他容器。
- 声明组件接口及其一系列方法,这些方法对简单和复杂元素都有意义。
- 创建一个叶节点类表示简单元素。程序中可以有多个不同的叶节点类。
- 创建一个容器类表示复杂元素。在该类中,创建一个数组成员变量来存储对于其子元素的引用。该数组必须能够同时保存叶节点和容器,因此请确保将其声明为组合接口类型。
- 实现组件接口方法时,记住容器应该将大部分工作交给其子元素来完成。
- 最后,在容器中定义添加和删除子元素的方法。
优缺点
优点
- 可以利用多态和递归机制更方便地使用复杂树结构。
- 开闭原则。无需更改现有代码,就可以在应用中添加新元素,使其成为对象树的一部分。
缺点
- 对于功能差异较大的类,提供公共接口或许会有困难。在特定情况下,需要过度一般化组件的接口,但这会使其变得令人难以理解。
与其它模式的关系
桥接模式
、状态模式
和策略模式
(在某种程度上包括适配器模式)的接口非常相似。- 实际上, 它们都基于
组合模式
——即将工作委派
给其他对象,不过也各自解决了不同的问题。 - 模式并不只是以特定方式组织代码的配方,还可以使用它们来和其他开发者讨论模式所解决的问题。
- 实际上, 它们都基于
- 可以在创建复杂组合树时使用
建造者模式
,可使其构造步骤以递归的方式运行。 责任链模式
通常和组合模式结合使用。- 在这种情况下,叶组件接收到请求后,可以将请求沿包含全体父组件的链一直传递至对象树的底部。
- 可以使用
迭代器模式
来遍历组合树。 - 可以使用
访问者模式
对整个组合树执行操作。 - 可以使用
享元模式
实现组合树的共享叶节点以节省内存。 - 组合和
装饰模式
的结构图很相似,因为两者都依赖递归组合来组织无限数量的对象。- 装饰类似于组合,但其只有一个子组件。
- 装饰为被封装对象添加了额外的职责,组合仅对其子节点的结果进行了 “求和”。
- 模式也可以相互合作:可以使用装饰来扩展组合树中特定对象的行为。
- 大量使用组合和装饰的设计通常可从对于
原型模式
的使用中获益。可以通过该模式来复制复杂结构,而非从零开始重新构造。
示例
用一个操作系统文件系统的例子来理解组合模式。
文件系统中有两种类型的对象:文件和文件夹。在某些情形中,文件和文件夹应被视为相同的对象。 这就是组合模式发挥作用的时候了。
假设需要在文件系统中搜索特定的关键词,这一搜索操作需要同时作用于文件和文件夹上。
- 对于文件而言,其只会查看文件的内容
- 对于文件夹则会在其内部的所有文件中查找关键词。
|
|
|
|
|
|
|
|