组合模式(Composite Pattern)
组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
在软件开发中我们经常会遇到处理部分与整体的情况,如我们经常见到的树形菜单,一个菜单项的子节点可以指向具体的内容,也可以是子菜单。类似的情况还有文件夹,文件夹的下级可以是文件夹也可以是文件。
组合模式可以很好地解决这类问题,组合模式通过让树形结构的叶子节点和树枝节点使用同样的接口,结合递归的思想来处理部分与整体关系,这种方式模糊了简单对象(叶子)和对象组(树枝)间的概念,让我们可以像处理单个对象一样去处理对象组。
优点:
- 高层模块(客户端)调用简单。组合模式使得客户端代码可以一致地处理单个对象和组合对象,无须关心自己处理的是单个对象,还是组合对象,这简化了客户端代码;
- 节点自由增加,更容易在组合体内加入新的对象,客户端不会因为加入了新的对象而更改源代码,满足“开闭原则”;
缺点:
- 在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则
- 设计较复杂,客户端需要花更多时间理清类之间的层次关系;
- 不容易限制容器中的构件;
- 不容易用继承的方法来增加构件的新功能;
组成元素:
- 抽象构件角色(Component):是组合中对象声明接口,实现所有类共有接口的默认行为。
- 树叶构件角色(_File):上述提到的单个对象,叶节点没有子节点。
- 树枝构件角色(_Folder):定义有子部件的组合部件行为,存储子部件,在Component接口中实现与子部件有关的操作。
- 客户端(Client):使用 Component 部件的对象。
抽象组合部件:
/// <summary>
/// 抽象组合部件
/// </summary>
public abstract class Component
{
protected string name; //部件名字
public Component(string name)
{
this.name = name;
}
public abstract Component add(Component component);
public abstract Component Remove(Component component);
public abstract Component Show(int index = 0);
}
具体组合部件:文件夹(树枝)
/// <summary>
/// 具体组合部件:文件夹
/// </summary>
public class _Folder : Component
{
List<Component> children = new List<Component>();
public _Folder(string name) : base(name) { }
public override Component add(Component component)
{
children.Add(component);
return this;
}
public override Component Remove(Component component)
{
children.Remove(component);
return this;
}
public override Component Show(int index = 0)
{
Debug.Log(new string(' ', index * 4) + "【" + name + "】");
foreach (Component component in children)
{
component.Show(index + 1);
}
return this;
}
}
具体组合部件:文件(树叶)
/// <summary>
/// 具体组合部件:文件
/// </summary>
public class _File : Component
{
public _File(string name) : base(name) { }
public override Component add(Component component)
{
Debug.Log("文件无此操作");
return this;
}
public override Component Remove(Component component)
{
Debug.Log("文件无此操作");
return this;
}
public override Component Show(int index)
{
Debug.Log(new string(' ', index * 4) + "=" + name);
return this;
}
}
运行测试:
void Start()
{
Component root = new _Folder("root");
root
.add(new _File("Leaf A"))
.add(new _File("Leaf B"))
.add(
new _Folder("Composite X")
.add(new _File("Leaf A"))
.add(new _File("Leaf B"))
)
.add(
new _Folder("Composite XY")
.add(new _File("Leaf XYA"))
.add(new _File("Leaf XYB"))
)
.add(new _File("Leaf C"))
.add(new _File("Leaf D"))
.Show()
;
}
结果: