组合模式
组合模式:将对象组合成树形结构以表示部分——整体的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。
组合模式,又称为合成模式,或者树模式等,从名称就可以看出,组合模式和树形结构以及递归有关系。
递归定义刻画了树的固有特性,一棵非空树是由若干棵子树构成的,而子树有可由若干棵更小的子树构成,这里的子树可以是叶子也可以是分支。
从定义中可以得到使用组合模式的环境为:在设计中想表示对象的“部分-整体”层次结构,希望用户忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象。
具体组成:
抽象构件角色(Component):它为组合中的对象声明接口,也可以为共有接口实现缺省行为。
树叶构件角色(Leaf):在组合中表示叶节点对象——没有子节点,实现抽象构件角色声明的接口。
树枝构件角色(Composite):在组合中表示分支节点对象——有子节点,实现抽象构件角色声明的接口,存储子部件。
不管你使用的是 Leaf 类还是 Composite 类,对于客户程序来说都是一样的——客户仅仅知道Component 这个抽象类。而且在Composite 类中还持有对Component抽象类的引用,这使得Composite 中可以包含任何Component 抽象类的子类。
组合模式的基本结构:
<span style="font-size:24px;">namespace 组合模式 { class Program { static void Main(string[] args) { Composite root = new Composite("root"); root.Add(new Leaf("Leaf A")); root.Add(new Leaf("Leaf B")); Composite comp = new Composite("Composite X"); comp .Add (new Leaf ("Leaf XA")); comp .Add (new Leaf ("Leaf XB")); root .Add (comp ); Composite comp2=new Composite ("Composite XY"); comp2.Add (new Leaf ("Leaf XYA")); comp2.Add(new Leaf("Leaf XYB")); comp.Add(comp2); root.Add(new Leaf("Leaf C")); Leaf leaf=new Leaf ("Leaf D"); root .Add (leaf ); root .Remove (leaf ); root .Display (1); Console .Read (); } } } //Component为组合中的对象声明接口,在适当情况下,实现所有类共有接口的默认行为 //声明一个接口用于访问和管理Component的子部件 abstract class Component { protected string name; public Component(string name) { this.name = name; } public abstract void Add(Component c); public abstract void Remove(Component c); public abstract void Display(int depth); } //Leaf在组合中表示叶节点对象,叶节点没有子节点 class Leaf : Component { public Leaf(string name) : base(name) { } public override void Add(Component c) { Console.WriteLine("Cannot add to a leaf"); } public override void Remove(Component c) { Console.WriteLine("Cannot remove from a leaf"); } public override void Display(int depth) { Console.WriteLine(new String('-', depth) + name); } } //Composite定义有枝节点行为,用来存储子部件 //在Component接口中实现与子部件有关的操作,比如增加Add和删除Remove class Composite : Component { private List<Component> children = new List<Component>(); public Composite(string name) : base(name) { } public override void Add(Component c) { children.Add(c); } public override void Remove(Component c) { children.Remove(c); } public override void Display(int depth) { Console .WriteLine (new string ('-',depth )+name ); foreach (Component component in children ) { component .Display (depth +2); } } } </span>
透明方式和安全方式:
组合模式中必须提供对子对象的管理方法,不然无法完成对子对象的添加删除等等操作,也就失去了灵活性和扩展性。
但是,管理方法是在 Component 中就声明还是在 Composite中声明,根据管理方法声明的位置不同,可以分为透明方式和安全方式。
一种方式是在 Component 里面声明,所有的用来管理子类对象的方法,以达到Component接口的最大化(如下图),目的就是为了使客户看来在接口层次上树叶和分支没有区别——透明性,但树叶是不存在子类的,因此Component 声明的一些方法对于树叶来说是不适用的,这样,也就带来了一些安全性的问题。
透明方式,就是说在Component中声明所有用来管理子对象的方法,其中包括Add、Remove等,这样实现Component接口的所有子类都具备了Add和Remove,这样做的好处就是叶节点和枝节点对于外界没有区别,它们具备完全一致的行为接口,但问题也很明显,因为Leaf类本身不具备Add()、Remove()方法的功能,所以实现它是没有意义的。
另一种方式就是只在 Composite 里面声明所有的用来管理子类对象的方法(如下图),这样就避免了上一种方式的安全性问题,但是由于叶子和分支有不同的接口,所以又失去了透明性。
安全方式,也就是在Component接口中不去声明Add和Remove方法,那么子类的Leaf也就不需要去实现它,而是在Composite声明所有用来管理子类对象的方法,这样做就不会出现刚才提到的问题,不过由于不够透明,所以树叶和树枝类将不具有相同的接口,客户端的调用需要做相应的判断,带来了不便。
什么时候使用组合模式:
当发现需求中是体现部分与整体层次的结构时,以及希望用户可以忽略组合对象与单个对象的不同,统一的使用组合结构中的所有对象时,就应该考虑用组合模式了。
组合模式的优点:
1、让客户可以一致的使用组合结构和单个对象。
2、使客户端调用简单,客户端可以一致的使用组合结构或其中单个对象,用户就不必关心自己处理的是单个对象还是整个组合结构,这就简化了客户端代码。
3、更容易在组合体内加入对象部件.客户端不必因为加入了新的对象部件而更改代码,这一点符合开闭原则的要求,对系统的二次开发和功能扩展很有利。
组合模式的缺点:组合模式不容易限制组合中的构件。
组合模式是一个应用非常广泛的设计模式,它本身比较简单但是很有内涵,掌握了它对我们的开发设计有很大的帮助。