组合模式(Composite Pattern):构建 “部分 - 整体” 的树形结构
组合模式是结构型设计模式的一种,核心思想是将对象组合成树形结构以表示 “部分 - 整体” 的层次关系,使客户端对单个对象(叶子节点)和组合对象(容器节点)的使用具有一致性。这种模式就像文件系统中的 “文件夹 - 文件” 结构,无论是单个文件还是文件夹(包含多个文件),都可以用统一的方式操作。
组合模式的核心结构
![组合模式]()
组合模式通过三个核心角色构建树形结构,实现对 “部分” 和 “整体” 的统一处理:
抽象组件(Component)
- 定义所有组件(叶子节点和组合节点)的公共接口,声明了添加、移除子组件、获取子组件及核心业务方法。
- 示例:
FileSystemNode(文件系统节点接口,定义getName()、getSize()等方法)。
叶子节点(Leaf)
- 表示树形结构中的最小单元(不可再包含子组件),实现抽象组件的接口,但不支持子组件操作(如添加、移除)。
- 示例:
File(文件,不可包含子节点)。
组合节点(Composite)
- 表示树形结构中的容器(可包含子组件,子组件可以是叶子节点或其他组合节点),实现抽象组件的接口,负责管理子组件(添加、移除、遍历)。
- 示例:
Folder(文件夹,可包含文件或子文件夹)。
代码实现示例
以 “文件系统” 为例,展示组合模式的实现:文件夹(Composite)可包含文件(Leaf)或子文件夹,客户端可统一获取大小、打印结构等。
1. 抽象组件(Component)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public abstract class FileSystemNode { protected String name;
public FileSystemNode(String name) { this.name = name; }
public abstract long getSize();
public String getName() { return name; }
public void addChild(FileSystemNode child) { throw new UnsupportedOperationException("不支持添加子节点"); }
public void removeChild(FileSystemNode child) { throw new UnsupportedOperationException("不支持移除子节点"); }
public List<FileSystemNode> getChildren() { throw new UnsupportedOperationException("不支持获取子节点"); } }
|
2. 叶子节点(Leaf)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class File extends FileSystemNode { private long size;
public File(String name, long size) { super(name); this.size = size; }
@Override public long getSize() { return size; } }
|
3. 组合节点(Composite)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public class Folder extends FileSystemNode { private List<FileSystemNode> children = new ArrayList<>();
public Folder(String name) { super(name); }
@Override public long getSize() { long totalSize = 0; for (FileSystemNode child : children) { totalSize += child.getSize(); } return totalSize; }
@Override public void addChild(FileSystemNode child) { children.add(child); }
@Override public void removeChild(FileSystemNode child) { children.remove(child); }
@Override public List<FileSystemNode> getChildren() { return new ArrayList<>(children); } }
|
4. 客户端使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public class CompositeDemo { public static void main(String[] args) { FileSystemNode file1 = new File("文档1.txt", 1024); FileSystemNode file2 = new File("图片1.png", 2048);
Folder folder1 = new Folder("工作资料"); folder1.addChild(file1); folder1.addChild(file2);
FileSystemNode file3 = new File("视频1.mp4", 4096); Folder folder2 = new Folder("娱乐资料"); folder2.addChild(file3); folder1.addChild(folder2);
System.out.println(folder1.getName() + "总大小:" + folder1.getSize() + "字节"); System.out.println(file1.getName() + "大小:" + file1.getSize() + "字节");
printStructure(folder1, 0); }
private static void printStructure(FileSystemNode node, int depth) { StringBuilder indent = new StringBuilder(); for (int i = 0; i < depth; i++) { indent.append(" "); } System.out.println(indent + "- " + node.getName() + "(" + node.getSize() + "字节)");
if (node instanceof Folder) { for (FileSystemNode child : ((Folder) node).getChildren()) { printStructure(child, depth + 1); } } } }
|
输出结果
1 2 3 4 5 6 7
| 工作资料总大小:7168字节 文档1.txt大小:1024字节 - 工作资料(7168字节) - 文档1.txt(1024字节) - 图片1.png(2048字节) - 娱乐资料(4096字节) - 视频1.mp4(4096字节)
|
组合模式的核心优势
- 统一处理 “部分” 和 “整体”
客户端无需区分叶子节点和组合节点,可通过相同的接口操作单个对象或整个树形结构(如获取大小、打印结构),简化了客户端代码。
- 灵活构建树形结构
可动态添加或移除子节点,轻松构建复杂的层次结构(如嵌套文件夹),符合开闭原则(扩展时无需修改现有代码)。
- 清晰的层次关系
通过树形结构直观表示 “部分 - 整体” 关系,使系统设计更符合现实世界的结构(如组织架构、文件系统)。
适用场景
- 存在 “部分 - 整体” 层次关系
当系统中的对象可自然地组织成树形结构,且需要统一操作单个对象和组合对象时(如:
- 文件系统:文件夹(整体)包含文件(部分)。
- 组织架构:部门(整体)包含员工(部分)或子部门。
- 菜单系统:主菜单(整体)包含子菜单(部分)或菜单项。
- 客户端需统一操作所有对象
客户端希望忽略 “部分” 和 “整体” 的差异,用相同的代码处理不同层级的对象(如计算总大小、遍历所有节点)。
- 需动态维护层次结构
系统需要支持动态添加、移除节点,或调整节点的层级关系(如动态生成菜单、调整部门结构)。
优缺点分析
优点
- 简化客户端代码:统一接口使客户端无需区分叶子和组合节点,减少条件判断。
- 易于扩展:新增叶子或组合节点时,只需实现抽象组件接口,无需修改现有代码。
- 清晰的层次表示:树形结构直观反映对象间的 “部分 - 整体” 关系,便于理解和维护。
缺点
- 设计复杂度增加:需要定义抽象组件并处理子节点操作,对简单场景可能显得冗余。
- 限制类型灵活性:若需严格区分叶子和组合节点的类型(如禁止某些操作),可能需要额外的类型检查。
经典应用案例
- GUI 组件框架
如 Swing 中的JComponent:JPanel(组合节点,可包含其他组件)和JButton(叶子节点)都继承自JComponent,客户端可统一添加到容器或设置属性。
- XML/HTML 文档解析
文档的 DOM 树结构:Element(组合节点,可包含子元素)和TextNode(叶子节点)通过统一接口操作,便于遍历或修改文档结构。
- 组织架构管理系统
部门(Department)包含员工(Employee)或子部门,可统一计算部门总人数、薪资总额等。
总结
组合模式通过抽象组件、叶子节点和组合节点的配合,完美实现了 “部分 - 整体” 层次结构的统一处理。其核心价值在于 “一致性”—— 让客户端用相同的方式操作单个对象和复杂组合对象,同时支持动态构建和维护树形结构。在需要表示层次关系且需统一操作的场景中,组合模式是简化设计、提高灵活性的理想选择