组合模式(Composite Pattern)是一种结构型设计模式。它允许你将对象组合成树形结构来表现“整体/部分”的层次结构。组合能让客户端以一致的方式处理个别对象(叶子)和对象组合(容器)。
在 C# 中实现组合模式,主要涉及三个角色:
我们以“文件系统”为例:文件夹(Composite)可以包含文件(Leaf),也可以包含其他文件夹。
这是所有对象的基类。为了保证“透明性”,我们通常在这里定义管理子节点的方法(Add/Remove),尽管叶子节点可能不需要它们。
using System; using System.Collections.Generic; namespace CompositePatternDemo { // 抽象构件 public abstract class FileSystemItem { protected string _name; public FileSystemItem(string name) { _name = name; } // 核心业务方法:所有节点都有这个功能 public abstract void Display(int depth); // 管理子节点的方法(虚方法,默认抛出异常,由容器节点重写) // 这种方式称为“透明方式”,客户端不需要区分是叶子还是容器 public virtual void Add(FileSystemItem item) { throw new NotImplementedException("叶子节点不支持添加子项"); } public virtual void Remove(FileSystemItem item) { throw new NotImplementedException("叶子节点不支持移除子项"); } } }
叶子节点代表具体的实体(如文件)。它不包含子节点列表。
namespace CompositePatternDemo { // 叶子节点:文件 public class File : FileSystemItem { public File(string name) : base(name) { } public override void Display(int depth) { // 通过连字符显示层级深度 Console.WriteLine(new String('-', depth) + " File: " + _name); } // File 不需要重写 Add 和 Remove,因为继承的默认行为是抛出异常,符合逻辑 } }
容器节点包含一个子节点列表,并实现了管理子节点的方法。关键点在于它的业务方法通常会递归调用子节点的业务方法。
namespace CompositePatternDemo { // 容器节点:文件夹 public class Directory : FileSystemItem { // 内部维护一个子组件列表 private List<FileSystemItem> _children = new List<FileSystemItem>(); public Directory(string name) : base(name) { } // 重写 Add 方法 public override void Add(FileSystemItem item) { _children.Add(item); } // 重写 Remove 方法 public override void Remove(FileSystemItem item) { _children.Remove(item); } // 核心:递归调用 public override void Display(int depth) { // 1. 显示自己 Console.WriteLine(new String('-', depth) + " Directory: " + _name); // 2. 遍历并显示所有子节点(递归) foreach (var component in _children) { component.Display(depth + 2); } } } }
客户端代码不需要关心它处理的是简单的 `File` 还是复杂的 `Directory`,它只需要针对 `FileSystemItem` 编程。
class Program { static void Main(string[] args) { // 1. 创建根节点 Directory root = new Directory("Root"); // 2. 创建叶子节点 File file1 = new File("Config.xml"); File file2 = new File("Logo.png"); // 3. 创建子容器 Directory subDir = new Directory("Bin"); File subFile = new File("App.exe"); // 4. 组装树形结构 root.Add(file1); root.Add(file2); subDir.Add(subFile); // 向子文件夹添加文件 root.Add(subDir); // 将子文件夹添加到根目录 // 5. 操作整体 // 客户端只需要调用根节点的 Display,整个树结构会自动递归处理 Console.WriteLine("文件系统结构图:"); root.Display(1); } }
在 C# 实现中,关于 `Add` 和 `Remove` 方法放在哪里,有两种流派:
组合模式的核心在于利用多态和递归。通过将容器和叶子抽象为同一类型,使得极其复杂的树形结构在客户端看来只是一个简单的对象。