一、什么是组合模式?
将对象组合成树形结构以表示‘部分-整体’的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
Client:客户端
Component:组合中的对象声明接口,在适当情况下,实现所有类共有接口的默认行为。声明一个接口用于访问和管理Component的子部件。
Leaf:在组合中表示叶节点对象,叶节点没有子节点。
Composite:定义有枝节点行为,用来存储子部件,在Component接口中实现与子部件有关的操作,比如增加Add和删除Remove。
两种模式:
透明方式
也就是说在Component中声明所有用来管理子对象的方法,其中包括Add、Remove等。这样实现Component接口的所有子类都具备了Add和Remove。这样做的好处就是叶节点和枝节点对于外界没有区别,他们具备完全一致的行为接口。但问题也很明显,因为Leaf类本身不具备Add()、Remove()方法的功能,所以实现它是没有意义的。
安全方式
也就是在Component接口中不去声明Add()和Remove方法,那么子类的Leaf也就不需要去实现它,而是在Composite声明所有用来管理子类对象的方法,不过由于不够透明,所以树叶和树枝类将不具有相同的接口,客户端的调用需要做相应的判断,带来了不便。
二、有什么优点?
高层模块调用简单。一棵树形机构中的所有节点都是Component,局部和整体对调用者来说没有任何区别,高层模块不必关心自己处理的是单个对象还是整个组合结构。
节点自由增加
三、有什么缺点?
使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒转原则
四、什么时候适用?
需求中是体现部分与整体层次的机构(树形结构)时;
用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有第项时
五、代码展示
①、组合模式
Component类
//为组合中的对象声明接口,在适当情况下,实现所有类共有接口的默认行为。 //声明一个接口用于访问和管理Component的子部件 abstract class Component { protected string name; //成员变量 public Component(string name) //有参构造方法 { this.name = name; } //通常都用Add和Remove方法来提供增加或移除树叶或树枝的功能 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); } } }