← 返回题目列表

什么是组合模式?它解决什么问题?

高频 简单 第 1 / 25 题 更新于 2026/07/28
组合模式结构型模式树形结构递归设计模式

简化版

组合模式是把对象组织成树形结构,让客户端可以用一致的方式处理单个对象和组合对象。它主要解决树形层级结构中“叶子节点”和“容器节点”处理方式不统一的问题。

详细版

组合模式的核心思想是:叶子对象和组合对象实现同一个抽象接口。

例如文件系统:

目录
  ├── 文件 A
  ├── 文件 B
  └── 子目录
       └── 文件 C

文件是叶子节点,目录是组合节点。但客户端希望统一调用:

node.getSize();
node.print();

不想每次判断:

if (node is File) ...
if (node is Directory) ...

组合模式适合组织架构、菜单树、文件目录、权限树、商品分类、UI 组件树等天然树形结构。

完整版教学

一、为什么树形结构容易写乱

很多业务都有树形结构:

  • 文件和目录;
  • 公司部门和员工;
  • 菜单和菜单项;
  • 商品分类;
  • 权限资源树;
  • UI 组件树;
  • 表达式语法树。

如果每一层都用不同接口,客户端就要写大量判断。

组合模式通过统一抽象,让客户端面对的是同一种组件。

二、组合模式的关键抽象

组合模式通常会定义一个公共组件接口:

interface Component {
    void operation();
}

叶子节点实现它:

class Leaf implements Component {
    public void operation() {
        // 叶子自己的操作
    }
}

组合节点也实现它:

class Composite implements Component {
    private List<Component> children;

    public void operation() {
        for (Component child : children) {
            child.operation();
        }
    }
}

这样组合节点可以递归调用子节点。

三、组合模式解决的是一致处理

组合模式最重要的价值不是“把对象放到树里”,而是让客户端一致处理整棵树。

例如:

Component root = buildTree();
root.operation();

客户端不关心 root 下面是文件、目录、子目录还是更深层结构。

四、组合模式天然适合递归

树结构通常有递归特征:

一个目录里可以有文件,也可以有目录
一个部门里可以有员工,也可以有子部门
一个菜单里可以有菜单项,也可以有子菜单

组合模式把这种递归关系映射到对象结构里。

组合对象持有子组件,子组件又可能是组合对象。

五、组合模式不是所有层级都要用

如果结构只有两层,而且业务很简单,直接写普通对象即可。

组合模式适合层级不固定、节点类型需要统一处理、递归操作较多的场景。

如果层级固定且逻辑简单,强行套组合模式会增加抽象成本。

六、常见误区与追问

组合模式把单个对象和对象集合放在同一抽象下。根目录含2个文件和1个子目录时,客户端只调用根的 size(),叶子返回自身大小,组合节点递归求和。它解决的是部分—整体的一致使用;若客户端仍到处 instanceof 判断节点类型,核心收益尚未实现。

检查维度判定依据
Leaf表示不可再分的部分
Composite表示整体并保存 Component 子节点

记忆钩子:叶子是“一个”,组合是“一组”,客户端用同一个 Component 看待二者。

  • 误区:组合模式只是普通的树数据结构。 它还要求叶子与容器共享抽象,使客户端能一致调用。
  • 追问:为什么它属于结构型模式? 它关注对象如何递归组合成部分—整体结构。
  • 误区:所有节点必须拥有完全相同的能力。 核心业务操作需统一,子节点管理能力可以采用安全式分离。
  • 追问:组合操作如何返回结果? 可对子节点求和、拼接或聚合,具体规则由业务语义决定。
  • 追问:什么时候会退化成类型判断? 公共抽象过弱,导致客户端必须识别具体 Leaf 或 Composite 才能工作。

七、加强记忆

组合模式的核心是“树形结构,一致处理”。叶子和容器实现同一个组件接口,客户端不用关心对象是单个节点还是组合节点,直接按统一方式操作整棵树。