组合模式(Composite Pattern)作为23种经典设计模式之一,属于结构型模式,其核心价值在于统一处理树形结构中的单个对象与组合对象,通过定义一致的接口,使客户端无需区分操作的是叶子节点还是分支节点。
一、组合模式的核心概念
1.适用场景
- 需要表示"部分-整体"层次结构(如文件系统、UI组件、组织架构)。
- 希望客户端统一处理单个对象和组合对象,避免冗余的条件判断。
- 支持递归操作(如遍历树形结构)。
2.核心角色
- Component(抽象组件):定义叶子节点和分支节点的公共接口,声明管理子节点的方法(如Add、Remove)。
- Leaf(叶子节点):表示单个对象,无子节点,实现Component接口但抛出异常或空实现管理子节点的方法。
- Composite(分支节点):包含子节点(叶子或分支),实现Component接口并管理子节点的增删查。
二、C#实现方式
组合模式在C#中有两种典型实现方式,各有优缺点:
1. 透明式组合模式
- 特点:在Component抽象类中声明所有管理子节点的方法(如Add、Remove),叶子节点和分支节点均实现这些方法(叶子节点抛出异常)。
- 优点:客户端可一致处理所有节点,无需区分类型。
- 缺点:叶子节点实现无意义的方法,可能引发运行时异常。
csharp
// 抽象组件
public abstract class Component
{
public string Name { get; set; }
public abstract void Add(Component component);
public abstract void Remove(Component component);
public abstract void Display(int depth);
}
// 叶子节点
public class Leaf : Component
{
public Leaf(string name) => Name = name;
public override void Add(Component component) =>
throw new InvalidOperationException("Leaf cannot add components.");
public override void Remove(Component component) =>
throw new InvalidOperationException("Leaf cannot remove components.");
public override void Display(int depth) =>
Console.WriteLine($"{new string('-', depth)}{Name}");
}
// 分支节点
public class Composite : Component
{
private List<Component> _children = new List<Component>();
public Composite(string name) => Name = name;
public override void Add(Component component) => _children.Add(component);
public override void Remove(Component component) => _children.Remove(component);
public override void Display(int depth)
{
Console.WriteLine($"{new string('-', depth)}{Name}");
foreach (var child in _children) child.Display(depth + 2);
}
}
// 客户端代码
var root = new Composite("Root");
root.Add(new Leaf("Leaf A"));
root.Add(new Leaf("Leaf B"));
var composite = new Composite("Composite X");
composite.Add(new Leaf("Leaf XA"));
root.Add(composite);
root.Display(1);
2. 安全式组合模式
- 特点:Component仅声明通用方法(如Display),管理子节点的方法(如Add、Remove)仅在Composite中定义。
- 优点:避免叶子节点实现无意义方法,类型安全。
- 缺点:客户端需区分节点类型,破坏透明性。
csharp
// 抽象组件(仅声明通用方法)
public abstract class Component
{
public string Name { get; set; }
public abstract void Display(int depth);
}
// 叶子节点
public class Leaf : Component
{
public Leaf(string name) => Name = name;
public override void Display(int depth) =>
Console.WriteLine($"{new string('-', depth)}{Name}");
}
// 分支节点(声明管理子节点的方法)
public class Composite : Component
{
private List<Component> _children = new List<Component>();
public Composite(string name) => Name = name;
public void Add(Component component) => _children.Add(component);
public void Remove(Component component) => _children.Remove(component);
public override void Display(int depth)
{
Console.WriteLine($"{new string('-', depth)}{Name}");
foreach (var child in _children) child.Display(depth + 2);
}
}
// 客户端需区分类型调用方法
var composite = new Composite("Composite");
composite.Add(new Leaf("Leaf")); // 合法
var leaf = new Leaf("Leaf");
leaf.Add(new Leaf("Leaf")); // 编译错误(类型安全)
三、组合模式的优缺点
1.优点
- 简化客户端代码:统一处理单个对象和组合对象,减少条件判断。
- 扩展性强:新增组件类型(如新图形)无需修改现有代码,符合开闭原则。
- 支持递归:方便遍历树形结构(如计算文件夹大小)。
2.缺点
- 过度使用导致复杂度增加:非层次结构场景强行使用会适得其反。
- 性能问题:大型树形结构的递归遍历可能引发栈溢出,需优化为迭代或缓存结果。
四、典型应用场景
- 图形编辑器:基本图形(圆、矩形)与组合图形(由多个基本图形组成)的统一绘制。
- 文件系统:文件与文件夹的统一操作(如删除、复制)。
- UI组件:按钮、文本框等控件与窗口的统一管理。
- 组织架构:员工与部门的统一权限控制。
五、与其他模式的对比
- 与装饰器模式区别:装饰器模式动态添加职责,组合模式构建树形结构。
- 与外观模式区别:外观模式简化复杂子系统调用,组合模式统一处理树形结构节点。
