Composite(组合) — 对象结构型模式
当有将对象组合成树形结构来表示“部分 - 整体” 的层次结构时,我们可以使用 Composite 模式,Composite 使得用户对单个对象和组合对象的使用具有一致性。
适用场景
- 你想表示对象的部分 - 整体层次结构。
- 你想用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的对象。
UML 图
效果
- 定义了包含基本对象和组合对象的类层次机构:基本对象可以被组合成更复杂的组合对象,而这个组合对象又可以被组合,这样不断地递归下去。客户代码中,任何用到基本对象的地方都可以使用组合对象。
- 简化客户代码:客户可以一致的使用组合结构和单个对象。通常用户不知道(也不关心)处理的是一个叶结点还是一个组合组件。这就简化了客户代码,因为在定义组合的那些类中不需要写一些充斥着选择语句的函数。
- 使得更容易增加新类型的组件:新定义的 Composite 或 Leaf 子类自动地与已有的结构的客户代码一起工作,客户程序不需要因新的 Component 类而改变。
- 使你的设计变得更加一般化:容易增加新组件也会产生一些问题,那就是很难限制组合中的组件。有时你希望一个组合只能有某些特定的组件。使用 Composite 时,你不能依赖类型系统施加这些约束,而必须在运行时进行检查。
几个问题
-
最大化 Component 接口:
Composite 模式的目的之一是使得用户不知道他们正在使用具体的 Leaf 类和 Composite 类。为了达到这一目的,Composite 类应为 Leaf 和 Composite 类尽可能多定义一些公共操作。Composite 类通常为这些操作提供缺省的实现,而 Leaf 和Composite 子类可以对它们进行重定义。
然而,这个目标有时可能会与类层次结构设计原则相冲突,该原则规定:一个类只能定义那些对它的子类有意义的操作。有许多 Component 所支持的操作对 Leaf 类似乎没有什么意义,那么 Component 怎样为它们提供一个缺省的操作呢?
即把 Leaf 看成一个没有子节点的 Component,就可以在 Component 类中定义一个缺省的操作。 -
声明管理子部件的操作:
虽然 Composite 类实现了 Add 和 Remove 操作用于管理子部件,但在 Composite 模式中一个重要的问题是:在 Composite 类层次结构中那些类声明这些操作。我们是应该在 Component 中声明这些操作,并使这些操作对 Leaf 类有意义,还是只应该在 Composite 和它的子类中声明并定义这些操作呢?
这需要在安全性和透明性之间做出选择。- 在类层次结构的根部定义子节点管理接口的方法具有良好的透明性,因为你可以一致的使用所有的组件,但是这一方法是以安全性为代价的,因为客户有可能会做一些无意义的事情,例如在 Leaf 中增加和删除对象等。
- 在 Composite 类中定义管理子部件的方法具有良好的安全性,因为在像 C++ 这样的静态类型语言中,在编译时任何从 Leaf 中增加或删除对象的尝试都会被发现。但这又损失了透明性,因为 Leaf 和 Composite 具有不同的接口。
在这一模式中,相对于安全性,我们比较强调透明性。如果你选择了安全性,有时你可能会丢失类型信息,并且不得不将一个组件转换成一个组合。这样的类型转换必定不是安全的。
示例
# coding=utf-8
class Component:def __init__(self, name):self.name = namedef add(self, comp):passdef remove(self, comp):passdef display(self, depth):passclass Leaf(Component):def display(self, depth):strtemp = ''for i in range(depth):strtemp += '---'print(strtemp + self.name)class Composite(Component):def __init__(self, name):super(Composite, self).__init__(name)self.children = []def add(self, comp):self.children.append(comp)def remove(self, comp):self.children.remove(comp)def display(self, depth):strtemp = ''for i in range(depth):strtemp += '---'print(strtemp + self.name)for comp in self.children:comp.display(depth + 2)
client
if __name__ == "__main__":root = Composite('根')root.add(Leaf('叶1a'))root.add(Leaf('叶1b'))root.add(Leaf('花1a'))root.add(Leaf('花1b'))comp1a = Composite('枝1a')comp1b = Composite('枝1b')root.add(comp1a)root.add(comp1b)comp1a.add(Leaf('叶2a'))comp1a.add(Leaf('叶2b'))comp1a.add(Leaf('花2a'))comp1a.add(Leaf('花2b'))comp2a = Composite('枝2a')comp1a.add(comp2a)comp1b.add(Leaf('叶2a'))comp1b.add(Leaf('花2a'))comp2a.add(Leaf('叶3a'))comp2a.add(Leaf('花3a'))comp3a = Composite('枝3a')comp2a.add(comp3a)comp3a.add(Leaf('叶4a'))comp3a.add(Leaf('花4a'))root.display(1)
""" output: ---根 ---------叶1a ---------叶1b ---------花1a ---------花1b ---------枝1a ---------------叶2a ---------------叶2b ---------------花2a ---------------花2b ---------------枝2a ---------------------叶3a ---------------------花3a ---------------------枝3a ---------------------------叶4a ---------------------------花4a ---------枝1b ---------------叶2a ---------------花2aProcess finished with exit code 0"""