首页/开发/代码质量与评审速查/组合模式 Composite(结构型)
设计原则与模式62 条

组合模式 Composite(结构型)

让「单个对象」与「组合对象」拥有一致的接口:树形结构(文件系统、组织架构、UI 容器)中调用方无需区分叶子与分支;注意约束可组合操作。

组合模式是什么

让单个对象与组合对象拥有同一套接口:调用方不必区分叶子与分支,递归结构(目录树、组织架构、界面容器、报表分组)可以用同一段代码处理。代价是接口必须覆盖两类对象——叶子被迫实现它用不到的增删子节点方法,这是个需要正视的取舍。

基本信息

意图树形结构的一致访问
角色Component(统一接口)/ Leaf / Composite
场景目录树、权限树、菜单、报表分组、表达式树
变体透明式(接口含增删)与安全式(只在 Composite 上暴露增删)

典型使用场景

  • 文件系统:文件与目录都提供 size 与遍历能力
  • 组织架构:部门与小组都能返回人数与预算合计
  • 界面容器:面板与控件都能响应渲染与布局
  • 权限树:单个权限与权限组都能判定是否包含某权限
  • 报表分组:明细行与分组行都能求和
  • 表达式树:常量与运算符节点都能求值

Java 示例

Java 示例
interface Node { long size(); }

class File implements Node {
    private final long bytes;
    File(long bytes) { this.bytes = bytes; }
    @Override public long size() { return bytes; }
}

class Dir implements Node {
    private final List<Node> children = new ArrayList<>();
    void add(Node child) { children.add(child); }
    @Override public long size() {
        return children.stream().mapToLong(Node::size).sum();   // 递归聚合
    }
}

接口设计取舍

方式做法代价
透明式增删方法放在统一接口上叶子必须实现或抛出不支持异常
安全式增删只在 Composite 上暴露调用方需要类型判断或转换
混合式统一接口加抽象的中间基类继承层次变深,需谨慎设计
递归遍历要有深度保护:用户可构造任意深度的结构时,退化链会引发栈溢出。改用显式栈的迭代遍历,或对输入深度设上限并在超限时明确报错。
结构被频繁遍历时要考虑缓存:每次查询都递归求和,在大树上代价明显。可缓存聚合结果并在结构变更时失效——注意失效必须覆盖所有变更入口,否则会返回过期数据。

易错点

  • 叶子对象被迫实现增删子节点方法:接口不诚实,调用方以为结构可变
  • 递归遍历无深度限制:退化链或环导致栈溢出,且难以复现
  • 统一接口过大:每组能力都塞进去,实现类要写一堆空方法
  • 聚合结果缓存失效不完整:变更入口漏掉一个,返回过期数据且难以排查
  • 结构允许成环:遍历陷入死循环,应当在构造或绑定父子关系时校验

数据来源与参考

  • 《设计模式》(GoF)· Composite
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌