组合模式通过抽象组件、叶子节点和组合节点三类角色,使单个对象与对象集合对外行为一致,调用统一接口时叶子直接执行、组合自动递归委托,无需重复遍历逻辑。

组合模式的关键在于让“单个对象”和“一堆对象”对外表现一致,调用同一个方法时,叶子节点直接执行,组合节点则自动递归调用所有子节点——不用写重复的遍历逻辑,树有多深,它就自动走多深。
明确三类角色,先搭骨架
所有实现都围绕三个核心角色展开:
-
抽象组件(Component):定义统一接口,比如
display()、calculateTotal()或save();只声明,不实现具体逻辑;可含默认空实现(如add()在叶子中抛异常) - 叶子节点(Leaf):真实业务对象,比如“文件”“菜单项”“商品SKU”,它不持有子节点,所有行为自己完成
- 组合节点(Composite):容器型对象,比如“文件夹”“菜单组”“商品分类”,内部维护子节点列表,对每个方法都遍历子节点并委托调用
选好典型场景,快速验证结构
初学建议从贴近日常的场景入手,例如:
-
文件系统模拟:Directory 是 Composite,File 是 Leaf,共用
printPath()接口——调用根目录的printPath(),自动打印出完整树状路径 -
UI 组件树:Panel 是 Composite,Button/Label 是 Leaf,共用
render()——任意层级调用 render,整棵树自上而下绘制 -
电商商品分类:Category 是 Composite,Product 是 Leaf,共用
getPriceRange()——叶子返回自身价格,组合返回子项 min/max 合并结果
这类场景天然具备“部分-整体”关系,接口语义清晰,调试时容易观察递归是否如期发生。
实现统一行为,重点在递归委托
行为统一不是靠条件判断,而是靠组合节点把调用“转发下去”。以 Java 中的 display(int depth) 为例:
- Leaf 实现:直接格式化输出内容,缩进由传入的
depth控制 - Composite 实现:先输出自身标识(如“文件夹:docs”),再对每个子节点调用
child.display(depth + 1) - 客户端代码全程无 if 判断节点类型,只管调用根节点的
display(0)
这种写法让新增叶子或组合完全透明——只要实现 Component 接口,就自动融入现有行为流。
注意边界与安全细节
实战中容易踩坑的地方:
- 避免无限递归:检查 Composite 的 add 方法是否允许加入自身或祖先节点,必要时加引用检测
-
操作权限收敛:像
add()、remove()这类管理方法,叶子节点应抛UnsupportedOperationException,而不是留空;否则运行时才发现错误 - 性能敏感时缓存结果:若某行为(如统计总数)被高频调用,可在 Composite 中加 dirty 标志+缓存字段,避免每次递归重算
- 类型限制可选但非必须:如果业务要求“文件夹里只能放文件和子文件夹”,可在 Composite 的 add 中做 instanceof 检查,但这会削弱模式的灵活性










