概念
组合模式(Composite Pattern)将对象组合成树状层次结构,使客户端对单个对象(叶子节点)和组合对象(容器节点)具有一致的访问方式。
核心思想:部分与整体的统一接口。
角色:
- Component:抽象组件,定义叶子和容器的公共接口
- Leaf:叶子节点,没有子节点,实现具体操作
- Composite:容器节点,包含子组件,将操作委托给子节点
示例:文件系统
文件系统是 Composite 模式的经典场景——文件夹可以包含文件或其他文件夹,但对外都提供统一的 getSize() 接口。
// Component
public interface FileSystemNode {
String getName();
long getSize();
void print(String indent);
}
// Leaf
public class File implements FileSystemNode {
private final String name;
private final long size;
public File(String name, long size) {
this.name = name;
this.size = size;
}
@Override
public String getName() {
return name;
}
@Override
public long getSize() {
return size;
}
@Override
public void print(String indent) {
System.out.println(indent + "📄 " + name + " (" + size + " bytes)");
}
}
// Composite
public class Directory implements FileSystemNode {
private final String name;
private final List<FileSystemNode> children = new ArrayList<>();
public Directory(String name) {
this.name = name;
}
public void add(FileSystemNode node) {
children.add(node);
}
public void remove(FileSystemNode node) {
children.remove(node);
}
@Override
public String getName() {
return name;
}
@Override
public long getSize() {
return children.stream()
.mapToLong(FileSystemNode::getSize)
.sum();
}
@Override
public void print(String indent) {
System.out.println(indent + "📁 " + name + "/");
for (FileSystemNode child : children) {
child.print(indent + " ");
}
}
}
// Client
public class Client {
public static void main(String[] args) {
Directory root = new Directory("root");
Directory src = new Directory("src");
src.add(new File("Main.java", 1200));
src.add(new File("Utils.java", 800));
Directory resources = new Directory("resources");
resources.add(new File("config.yml", 300));
resources.add(new File("banner.txt", 50));
root.add(src);
root.add(resources);
root.add(new File("README.md", 500));
root.print("");
System.out.println("Total size: " + root.getSize() + " bytes");
}
}
输出:
📁 root/
📁 src/
📄 Main.java (1200 bytes)
📄 Utils.java (800 bytes)
📁 resources/
📄 config.yml (300 bytes)
📄 banner.txt (50 bytes)
📄 README.md (500 bytes)
Total size: 2850 bytes
客户端调用 root.getSize() 和 file.getSize() 的方式完全相同,无需区分是文件夹还是文件。
适用场景
- 树形结构:文件系统、组织架构、菜单、DOM 树
- 需要统一处理叶子和容器:无论节点类型,调用方式一致
- 递归操作:遍历、统计、渲染整棵树
与其他模式的关系
| 模式 | 关系 |
|---|---|
| Builder | Builder 有时用于构建 Composite 树结构 |
| Iterator | 可用于遍历 Composite 树 |
| Visitor | 可在不修改 Composite 结构的前提下添加新操作 |
| Decorator | 与 Composite 都依赖递归组合,但 Decorator 只有一个子节点 |