代码界的「俄罗斯套娃」:组合模式的嵌套艺术
一、当对象开始「装孙子」
你是否见过这样的代码奇观?
文件夹里套着子文件夹,子文件夹又装着文件,文件里存着文件列表...
公司部门包含子部门,子部门管理着团队,团队里又有员工...
游戏技能树的主技能点开是子技能,子技能还能展开更多分支...
组合模式就像代码界的套娃大师------「大的装小的,小的装更小的」,让客户端用同一套姿势盘整个体与组合对象,体验「一即是全,全即是一」的哲学快感。
二、套娃工厂的设计图纸(UML图)
java
┌─────────────┐
│ Component │
├─────────────┤
│ +operation()│
│ +add() │
│ +remove() │
└──────△──────┘
│
┌────────┴─────────┐
│ │
┌─────────────┐ ┌─────────────┐
│ Leaf │ │ Composite │
└─────────────┘ ├─────────────┤
│ +children │
└─────────────┘
- 套娃模具(Component):定义统一接口
- 最小套娃(Leaf):不能再拆分的原子对象
- 套娃容器(Composite):能装其他套娃的盒子
三、文件系统的套娃表演(代码实战)
1. 定义套娃接口
java
// 文件系统元素抽象(所有套娃的模具)
interface FileSystemComponent {
void showDetails(int indent);
default void addComponent(FileSystemComponent comp) {
throw new UnsupportedOperationException("叶子节点不能装东西!");
}
}
2. 制造最小套娃(文件)
java
class File implements FileSystemComponent {
private String name;
public File(String name) { this.name = name; }
public void showDetails(int indent) {
System.out.println(" ".repeat(indent) + "📄 " + name);
}
}
3. 制作套娃盒子(文件夹)
java
class Folder implements FileSystemComponent {
private String name;
private List<FileSystemComponent> children = new ArrayList<>();
public Folder(String name) { this.name = name; }
public void showDetails(int indent) {
System.out.println(" ".repeat(indent) + "📁 " + name);
children.forEach(child -> child.showDetails(indent + 2));
}
public void addComponent(FileSystemComponent comp) {
children.add(comp);
}
}
4. 开始套娃表演
java
public class MatryoshkaShow {
public static void main(String[] args) {
// 创建文件系统套娃
Folder root = new Folder("我的电脑");
Folder documents = new Folder("文档");
documents.addComponent(new File("简历.doc"));
documents.addComponent(new File("账单.xlsx"));
Folder photos = new Folder("照片");
photos.addComponent(new File("毕业照.jpg"));
photos.addComponent(new File("团建合影.jpg"));
root.addComponent(documents);
root.addComponent(photos);
root.addComponent(new File("readme.txt"));
// 递归展示套娃结构
root.showDetails(0);
}
}
/* 输出:
📁 我的电脑
📁 文档
📄 简历.doc
📄 账单.xlsx
📁 照片
📄 毕业照.jpg
📄 团建合影.jpg
📄 readme.txt
*/
四、套娃 vs 堆积木:组合与聚合的区别
维度 | 组合模式 | 普通聚合 |
---|---|---|
统一接口 | 叶子与容器使用相同接口 | 容器与内容对象接口不同 |
递归处理 | 天然支持递归操作 | 需要额外处理嵌套结构 |
客户端视角 | 无需区分个体与组合 | 需要判断对象类型 |
扩展性 | 容易添加新组件类型 | 添加新类型影响客户端 |
现实类比 | 俄罗斯套娃 | 乐高积木 |
五、代码套娃的真实世界
- GUI组件树:窗口→面板→按钮→图标
- 电商分类系统:大类→子类→商品
- 组织结构管理:公司→部门→团队→员工
- XML/JSON解析:节点→子节点→属性
- 游戏装备系统:背包→盒子→药水
冷知识 :
Java的Swing框架中,JComponent
就是组合模式的典型代表,所有组件都可以嵌套。
六、防套娃翻车指南
- 透明性陷阱:
java
// 错误示范:让文件支持add操作
File resume = new File("简历.doc");
resume.addComponent(...); // 抛出异常!
- 循环引用检测:
java
public void addComponent(FileSystemComponent comp) {
if (isAncestor(comp)) {
throw new IllegalArgumentException("禁止套娃循环!");
}
children.add(comp);
}
- 缓存优化:
java
// 缓存计算密集型操作(如文件夹大小)
private long cachedSize;
public long calculateSize() {
if (cachedSize == 0) {
cachedSize = children.stream().mapToLong(FileSystemComponent::calculateSize).sum();
}
return cachedSize;
}
- 访问者模式联用:
java
interface FileSystemVisitor {
void visitFile(File file);
void visitFolder(Folder folder);
}
class Folder implements FileSystemComponent {
// ...
public void accept(FileSystemVisitor visitor) {
visitor.visitFolder(this);
children.forEach(child -> child.accept(visitor));
}
}
- 安全模式选择:
java
// 安全模式:拆分Component接口
interface FileSystemComponent { void showDetails(); }
interface ContainerComponent extends FileSystemComponent {
void addComponent(FileSystemComponent comp);
}
七、套娃艺术总结
组合模式让代码成为优雅的嵌套大师:
- ✅ 要:用于树形结构且需要统一操作的场景
- ✅ 要:区分透明模式与安全模式
- ❌ 不要:让叶子节点支持容器操作
- ❌ 不要:忽视循环引用的风险
当你在IDE中点开项目结构树时,请想起组合模式------那个让你优雅处理文件嵌套的隐形套娃师!