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 = name
def add(self, comp):
pass
def remove(self, comp):
pass
def display(self, depth):
pass
class 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
---------------花2a
Process finished with exit code 0
"""