什么是组合模式呢?简单来说组合模式就是将对象合成树形结构以表示“部分整体”的层次结构,组合模式使用户对单个对象和组合对象使用具有一致性。
组合模式(Composite Pattern)有时候又叫部分-整体模式,它使我们在树型结构的问题中,模糊了简单元素和负责元素的概念,客户程序可以向处理简单元素一样处理负责元素,从而使得客户程序与复杂元素的的内部结构解耦。
组合模式让你可以优化处理递归或分级数据结构。关于分级数据结构的一个经典例子就是电脑中的文件系统。文件系统由目录和文件组成,所有目录都可以有子目录和文件。实际上文件系统就是按照递归来组织的,那么就可以组合模式来描述这种结构。
适用性
以下情况下适用Composite模式:
- 想表示对象的部分—整体层次结构
- 希望用户忽略组合对象与单个对象的不同,用户将统一地使用组合结构中的所有对象。
组合模式解耦了客户程序与复杂元素内部结构,从而使客户程序可以向处理简单元素一样来处理复杂元素。如果你想要创建层次结构,并可以在其中以相同的方式对待所有元素,那么组合模式就是最理想的选择。
组成模式的实现根据实现接口的区别分为两种形式,分别称为安全模式和透明模式。合成模式可以不提供父对象的管理方法,但合成模式必须在合适的地方提供子对象的管理方法(如:add,remove,getchild等)。
透明方式:
作为第一种选择,在Component里面声明所有的用来管理子对象的方法,包括Add(),Remove(),以及GetChild()方法。这样做的好处是所有的构件类都有相同的接口,在客户端看来,树叶类对象与合成类对象的区别起码在接口层次上消失了,客户端可以等同的对待所有对象,这就是透明形式的合成模式。
这个选择的缺点是不够安全,因为树叶类对象和合成类对象在本质上是有区别的。树叶类对象不可能有下一个层次的对象,因此Add(),Remove(),GetChild()方法没有意义,是在编译时期不会出错,而只会在运行时期出错。
安全方式
安全方式是在Composite类里面声明所有的用来管理子对象的方法,这样的做法是安全的做法,因为树叶类型的对象根本就没有管理子对象的方法,因此,如果客户端对树叶类对象使用这些方法,程序会在编译时期出错。 这个方式的缺点是不够透明,因为树叶类和合成类将具有不同的接口。
这种形式涉及到三个角色:
- 抽象构件(Component)角色:这是一个抽象角色,它给参加组合的对象定义出公共接口及默认行为,可以用来管理所有的子对象。在安全形式的合成模式里,抽象构件角色并不定义出管理子对象的方法,这一定义由树枝构件定义。
- 树叶构件(Leaf)角色: 树叶对象是没有下级子对象的对象,定义出参加组合的原始对象的行为。
- 树枝构件(Composite)角色: 代表参加组合的有下级子对象的对象。树枝给出所有的管理子对象的方法,如Add(),Remove(),GetChild()等。
实现代码:
public abstract class Component
{
protected string name;
public Component(string name)
{
this.name = name;
}
public abstract void Display(int index);
}
public class Leaf : Component
{
public Leaf(string name) : base(name) { }
public override void Display(int index)
{
Console.WriteLine("Leaf" + index);
}
}
public class Composite : Component
{
public Composite(string name) : base(name) { }
public override void Display(int index)
{
Console.WriteLine("Composite" + index);
foreach (Component c in lst)
{
c.Display( index);
}
}
IList<Component> lst = new List<Component>();
public void Add(Component ic)
{
lst.Add(ic);
}
public void Remove(Component ic)
{
lst.Remove(ic);
}
}
public class Test
{
public static void Main()
{
Composite root = new Composite("Root");
root.Add(new Leaf("Leaf A"));
root.Add(new Leaf("Leaf B"));
Composite com = new Composite("Composite X");
com.Add(new Leaf("Leaf XA"));
com.Add(new Leaf("Leaf XB"));
root.Add(com);
}
}
透明式的合成模式结构
与安全式的合成模式不同的是,透明式的模式要求所有的具体构件类,不论是树枝构件还是树叶构件,均有一个固定的接口。
这种形式涉及到三个角色:
- 抽象构件(Component)角色:这是一个抽象角色,它给所有参加组合的对象规定一个固定的接口,规范共有的接口及默认行为.
- 树叶构件(Leaf)角色:代表参加组合的树叶对象,定义出参加组合的原始对象的行为,树叶类会给出Add(),Remove(),以及GetChild()之类的用来管理子类对象的方法的实现。
- 树枝构件角色(Composite)角色: 代表参加组合的有子对象的对象。定义出这样的对象的行为。
具体的实现代码很简单,这里就不写了。
使用合成模式时考虑的几个问题:
- Composite模式采用树形结构来实现普遍存在的对象容器,从而将“一对多”的关系转换为“一对一”的关系,使得客户代码可以一致地处理对象和对象容器,无需关系处理的是单个对象还是组合的对象容器。
- 将客户代码与复杂的对象容器解耦是合成模式的核心思想,解耦之后,客户代码将与纯粹的抽象接口—----非对象容器的内部实现结构发生依赖关系。
- 有时候系统需要遍历一个树枝结构的子构件很多次,这时候可以考虑把遍历子构件的结构暂时存储在父构件里面作为缓存。
- Composite模式中,是将Add和Remove等和对象容器相关的方法定义在“表示抽象的Componont类”中,还是定义在“表示对象容器的Composite类”中,是一个关乎“透明性”和“安全性”的两难问题,需要仔细权衡。