组合模式 (Composite Pattern)
什么是组合模式?
组合模式是一种结构型设计模式,它允许你将对象组合成树形结构来表示"部分-整体"的层次结构。组合模式让客户端可以统一地处理单个对象和对象组合。
简单来说:组合模式就是让单个对象和组合对象具有一致的行为。
生活中的例子
想象一下:
- 文件系统:文件和文件夹都可以被删除、复制、移动
- 组织架构:员工和部门都可以被管理
- 菜单系统:菜单项和子菜单都可以被点击
为什么需要组合模式?
传统方式的问题
java
// 需要分别处理单个对象和组合对象
if (object instanceof File) {
file.delete();
} else if (object instanceof Folder) {
folder.delete();
// 还需要删除文件夹中的所有文件
}
问题:
- 代码重复:需要分别处理单个对象和组合对象
- 难以维护:修改一个逻辑需要修改多处代码
- 客户端复杂:客户端需要知道对象的类型
组合模式的优势
java
// 统一处理单个对象和组合对象
node.delete(); // 不管是文件还是文件夹,都调用同一个方法
优势:
- 统一处理:客户端可以统一处理单个对象和组合对象
- 简化代码:简化客户端代码
- 易于扩展:新增节点类型很容易
组合模式的结构
┌─────────────────────┐
│ Component │ 组件接口
├─────────────────────┤
│ + operation(): void │
│ + add(): void │
│ + remove(): void │
│ + getChild(): Comp │
└──────────┬──────────┘
│ 继承
├──┬──────────────────┬──────────────┐
│ │ │
┌──────────┴──────┐ ┌───────────┴───────┐ ┌───┴────────┐
│ Leaf │ │ Composite │ │ ... │
├─────────────────┤ ├───────────────────┤ ├────────────┤
│ + operation() │ │ - children: List │ │ │
│ + add() │ │ + operation() │ │ │
│ + remove() │ │ + add() │ │ │
│ + getChild() │ │ + remove() │ │ │
└─────────────────┘ │ + getChild() │ │ │
└───────────────────┘ └────────────┘
代码示例
1. 定义组件接口
java
/**
* 文件系统节点接口
*/
public interface FileSystemNode {
/**
* 显示节点信息
*/
void display();
/**
* 添加子节点
*/
void add(FileSystemNode node);
/**
* 移除子节点
*/
void remove(FileSystemNode node);
/**
* 获取子节点
*/
FileSystemNode getChild(int i);
}
2. 定义叶子节点
java
/**
* 叶子节点:文件
*/
public class File implements FileSystemNode {
private String name;
private long size;
public File(String name, long size) {
this.name = name;
this.size = size;
}
@Override
public void display() {
System.out.println(" 文件: " + name + " (" + size + " bytes)");
}
@Override
public void add(FileSystemNode node) {
throw new UnsupportedOperationException("文件不能添加子节点");
}
@Override
public void remove(FileSystemNode node) {
throw new UnsupportedOperationException("文件不能移除子节点");
}
@Override
public FileSystemNode getChild(int i) {
throw new UnsupportedOperationException("文件没有子节点");
}
}
3. 定义组合节点
java
/**
* 组合节点:文件夹
*/
public class Folder implements FileSystemNode {
private String name;
private List<FileSystemNode> children = new ArrayList<>();
public Folder(String name) {
this.name = name;
}
@Override
public void display() {
System.out.println("文件夹: " + name);
for (FileSystemNode child : children) {
child.display();
}
}
@Override
public void add(FileSystemNode node) {
children.add(node);
}
@Override
public void remove(FileSystemNode node) {
children.remove(node);
}
@Override
public FileSystemNode getChild(int i) {
return children.get(i);
}
}
4. 使用组合
java
/**
* 组合模式测试类
* 演示如何使用组合模式处理树形结构
*/
public class CompositeTest {
public static void main(String[] args) {
System.out.println("=== 组合模式测试 ===\n");
// 创建文件
File file1 = new File("readme.txt", 1024);
File file2 = new File("image.jpg", 2048);
File file3 = new File("document.pdf", 4096);
File file4 = new File("data.json", 512);
// 创建文件夹
Folder folder1 = new Folder("文档");
Folder folder2 = new Folder("图片");
Folder folder3 = new Folder("项目");
// 构建文件系统树
folder1.add(file1);
folder1.add(file3);
folder2.add(file2);
folder3.add(folder1);
folder3.add(folder2);
folder3.add(file4);
// 显示文件系统
System.out.println("--- 文件系统结构 ---");
folder3.display();
System.out.println("\n=== 组合模式的优势 ===");
System.out.println("1. 统一处理:客户端可以统一处理单个对象和组合对象");
System.out.println("2. 简化代码:简化客户端代码");
System.out.println("3. 易于扩展:新增节点类型很容易");
System.out.println("4. 层次清晰:清晰地表示层次结构");
System.out.println("\n=== 实际应用场景 ===");
System.out.println("1. 文件系统:文件和文件夹");
System.out.println("2. 组织架构:员工和部门");
System.out.println("3. 菜单系统:菜单项和子菜单");
System.out.println("4. XML/JSON解析:节点和子节点");
}
}
组合模式的优点
- 统一处理:客户端可以统一处理单个对象和组合对象
- 简化代码:简化客户端代码
- 易于扩展:新增节点类型很容易
- 层次清晰:清晰地表示层次结构
组合模式的缺点
- 设计复杂:设计比较复杂
- 限制类型:限制了组件的类型
适用场景
- 树形结构:需要表示树形结构
- 统一处理:需要统一处理单个对象和组合对象
- 层次结构:需要表示部分-整体的层次结构
常见应用场景
- 文件系统:文件和文件夹
- 组织架构:员工和部门
- 菜单系统:菜单项和子菜单
- XML/JSON解析:节点和子节点
使用建议
- 树形结构:使用组合模式
- 统一处理:使用组合模式
- 简单结构:直接使用即可
注意事项
⚠️ 组合模式虽然有用,但要注意:
- 叶子节点的add和remove方法可以抛出异常
- 考虑使用透明性或安全性模式