2.4 组合模式
组合模式(Composite)让你把对象组织成树形结构,并对“单个对象”和“对象的组合”一视同仁地处理。文件系统是最经典的例子:文件是叶子,文件夹是容器,但当你问“占用多大空间”时,你希望对文件和文件夹用同一个方法去问。
它的核心在于:叶子和容器实现同一个接口。客户端拿到一个节点,根本不需要判断它是文件还是文件夹,直接调用 size(),递归会自动展开整棵子树。
下面的文件树:点击文件夹展开/折叠,每个节点右侧的大小都是“子节点大小之和”递归算出来的。
正在加载交互实验...
统一接口是关键
java
interface Node {
int size();
}
class File implements Node {
public int size() { return bytes; } // 叶子:直接返回
}
class Folder implements Node {
private List<Node> children = new ArrayList<>();
public int size() {
return children.stream().mapToInt(Node::size).sum(); // 容器:递归汇总
}
}Folder.size() 把请求转发给每个子节点,而子节点可能又是 Folder——递归就这样自然形成。
不止文件树
任何“部分-整体”层级都适用:组织架构的薪资汇总、UI 控件的嵌套布局、订单里的套餐与单品、菜单与子菜单。下面的预算实验让你调整各个小组的人数(叶子),观察 CEO 节点的总预算如何自下而上递归重算。
正在加载交互实验...
正在加载概念检查...
设计取舍:透明 vs 安全
组合模式有一个经典争议——add(child)、remove(child) 这些方法该放哪?
- 透明方式:把
add/remove放在公共接口Node上。客户端对叶子和容器完全一致,但对叶子调用add()是没有意义的(只能抛异常或空实现)。
- 安全方式:只在
Folder上声明add/remove。语义更安全,但客户端有时需要判断类型。
没有标准答案,取决于你更看重“一致性”还是“类型安全”。
正在加载概念检查...
正在加载本节练习...