《组合模式》|设计模式系列第09篇
系列:结构型模式 · 组合模式(Composite Pattern)
关键词:树形结构、部分-整体、叶子节点、容器节点、统一递归访问
一、什么是组合模式
组合模式(Composite Pattern):将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

属于结构型设计模式。
简单理解:
把叶子节点(单个对象)和容器节点(包含子节点的对象)实现同一套接口。客户端不需要区分当前操作的是单个对象,还是包含一堆子对象的容器,调用方式完全一致。典型场景:文件目录树,文件夹里面可以放文件,也可以放子文件夹;文件和文件夹对外都支持展示名称、删除等操作。
核心意图
统一叶子对象与组合对象的访问方式,方便操作树形结构,客户端不用区分节点类型。
适用场景
- 需要表示对象的部分-整体树形层次结构
- 希望客户端可以统一对待单个对象和组合对象
- 遍历树形结构,递归处理节点
- 菜单、组织架构、文件目录、权限树等场景
⚠️ 缺点预警:设计较抽象;如果节点类型差异很大,很难定义统一接口;限制叶子节点和容器节点的行为。
二、UML结构图
角色划分:
- Component(抽象构件):叶子和容器统一的抽象接口,声明公共操作
- Leaf(叶子构件):叶子节点,没有子节点,实现Component接口
- Composite(容器构件):容器节点,包含子节点集合,实现Component接口,管理子节点
- Client(客户端):面向Component抽象编程,无需区分叶子/容器

三、Java 完整示例
场景:文件系统。文件(叶子)、文件夹(容器),统一支持展示名称,文件夹可以增加子节点。
1. 抽象构件 Component
public abstract class FileSystemNode {
protected String name;
public FileSystemNode(String name) {
this.name = name;
}
public abstract void show(int depth);
// 容器节点才需要的方法,叶子节点空实现
public boolean add(FileSystemNode node) {
throw new UnsupportedOperationException();
}
public boolean remove(FileSystemNode node) {
throw new UnsupportedOperationException();
}
}
2. 叶子构件 Leaf(文件)
public class File extends FileSystemNode {
public File(String name) {
super(name);
}
@Override
public void show(int depth) {
// 打印缩进,模拟层级
for (int i = 0; i < depth; i++) {
System.out.print("-");
}
System.out.println(name);
}
}
3. 容器构件 Composite(文件夹)
import java.util.ArrayList;
import java.util.List;
public class Folder extends FileSystemNode {
private List<FileSystemNode> childList = new ArrayList<>();
public Folder(String name) {
super(name);
}
@Override
public boolean add(FileSystemNode node) {
childList.add(node);
return true;
}
@Override
public boolean remove(FileSystemNode node) {
childList.remove(node);
return true;
}
@Override
public void show(int depth) {
for (int i = 0; i < depth; i++) {
System.out.print("-");
}
System.out.println(name);
// 递归展示子节点,层级+1
for (FileSystemNode child : childList) {
child.show(depth + 1);
}
}
}
4. 客户端调用
public class Client {
public static void main(String[] args) {
FileSystemNode root = new Folder("根目录");
FileSystemNode docFolder = new Folder("文档");
FileSystemNode picFolder = new Folder("图片");
FileSystemNode readme = new File("readme.txt");
FileSystemNode photo = new File("风景.jpg");
docFolder.add(readme);
picFolder.add(photo);
root.add(docFolder);
root.add(picFolder);
// 统一调用show,不用区分是文件还是文件夹
root.show(0);
}
}
客户端只依赖 FileSystemNode,调用 show(),不管是文件还是文件夹,调用方式完全一样。
四、两种实现方式
- 透明模式(示例代码这种):
add/remove放在抽象构件。叶子节点继承后抛出异常;优点:客户端完全不用区分节点;缺点:叶子有无效方法。 - 安全模式:
add/remove只放在 Composite 容器。优点:叶子不会有无效方法;缺点:客户端需要判断类型才能增删子节点。
开发常用透明模式,牺牲一点接口纯净度换取客户端使用一致性。
五、优缺点总结
✅ 优点
- 统一操作叶子与容器对象,简化客户端代码
- 树形结构扩展方便,新增叶子/容器都不需要修改原有代码,符合开闭原则
- 方便递归遍历树形数据
❌ 缺点
- 抽象设计难度高,很难为所有构件定义统一行为接口
- 透明模式下叶子节点会拥有不需要的 add/remove 方法,违背接口隔离原则
六、框架源码应用
- JDK
java.awt.Container/ Component - MyBatis SqlNode,动态SQL节点树,大量使用组合模式
- Spring CompositeFileListFilter
七、小结
组合模式核心:统一叶子和容器的接口,用树形结构表达部分与整体,客户端无差别调用。
适合树形层级结构,比如菜单、组织树、文件目录。核心判断点:是否需要对单个对象和一组对象做相同操作。
0 条笔记