组合模式
意图
将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。
使用场景
以下情况使用Composite模式:
- 你想表示对象的部分-整体层次结构。
- 你希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
结构
组合模式结构如下
Component
–为组合中的对象声明接口。
–在适当的情况下,实现所有类共有接口的缺省行为。
–声明一个接口用于访问和管理Component的子组件。
–(可选)在递归结构中定义一个接口,用于访问一个父部件,并在合适的情况下实现它。Leaf
– 在组合中表示叶节点对象,叶节点没有子节点。
– 在组合中定义图元对象的行为。Composite
–定义有子部件的那些部件的行为。
–存储子部件。
–在Component接口中实现与子部件有关的操作。Client
–通过Component接口操纵组合部件的对象。
协作
用户使用Component类接口与组合结构中的对象进行交互。如果接收者是一个叶节点,则直接处理请求。如果接收者是Composite, 它通常将请求发送给它的子部件,在转发请求之前与/或之后可能执行一些辅助操作。
效果
- 定义了包含基本对象和组合对象的类层次结构
基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断的递归下去。客户代码中,任何用到基本对象的地方都可以使用组合对象。 - 简化客户代码
客户可以一致地使用组合结构和单个对象。通常用户不知道(也不关心)处理的是一个叶节点还是一个组合组件。这就简化了客户代码, 因为在定义组合的那些类中不需要写一些充斥着选择语句的函数。 - 使得更容易增加新类型的组件
新定义的Composite或Leaf子类自动地与已有的结构和客户代码一起工作,客户程序不需因新的Component类而改变。 - 使你的设计变得更加一般化
容易增加新组件也会产生一些问题,那就是很难限制组合中的组件。有时你希望一个组合只能有某些特定的组件。使用Composite时,你不能依赖类型系统施加这些约束,而必须在运行时刻进行检查。
组合模式实现(Implement)
案例
其实在文件系统中,文件夹也是一种文件,现在我们把文件夹跟文件都抽象成AbstractFile类,文件夹为Directory类,具体的文件为File类,文件夹有增加文件(夹),删除文件(夹),清空文件夹和显示文件夹目录下所有内容,文件可以显示当前文件名。
代码实现
抽象文件类
文件夹类
文件类
Client调用
父类跟子类之间的层次关系十分清楚,而且公共的接口也是一致的,但是出现了一个新的问题,那就是在创建文件夹和文件对象时必须指定创建类型Directory和File,而不能使用抽象AbstractFile,这样就违背了依赖倒置原则,Client依赖于具体的实现而不是抽象。这种方式叫作安全的组合模式,还有一种组合模式叫作透明的组合模式,将所有方法都在抽象中声明,但是实现类比如File类不需要的方法像addAbstractFile()就抛出不支持操作类型的异常,这样的话依赖问题是解决了,但是程序在运行时将变得不安全,一旦调用到子类不支持的方法程序将抛出异常。透明的组合模式代码如下
抽象文件类
文件夹类
文件类
Client调用透明的组合模式
总结
在组合模式中最重要的一点是整体与部分的层次关系,强调的是上级跟下级实现相同的接口,在调用的时候具有一致性。组合模式可以形成树状结构,并且对树状结构的操作非常简单。