组合模式比继承更灵活,能避免类臃肿、支持行为插拔与动态替换,适用于“整体-部分”结构和“has-a/can-do”关系,需分层使用并警惕间接性陷阱。

组合模式比继承更灵活,能有效避免类因过度继承而变得臃肿、难以维护。关键不是“能不能继承”,而是“要不要继承”——当行为可插拔、结构可动态变化时,组合往往是更自然的选择。
用组合代替“父类塞满所有功能”的继承
常见误区是把所有可能用到的功能都堆进基类,子类再通过继承“被动接收”。结果基类越来越胖,子类却只用其中一两个方法。组合模式把功能拆成独立组件,类按需装配。
- 比如“游戏角色”不再继承“会飞+会游泳+会隐身”的超类,而是持有 FlightBehavior、SwimBehavior、StealthBehavior 等接口实现
- 运行时可随时替换行为:
character.setFlightBehavior(new RocketFlight()),无需改类结构 - 新增行为(如 TeleportBehavior)不改动任何已有类,符合开闭原则
让容器与叶子统一对待:树形结构天然适合组合
当对象存在“整体-部分”关系(如文件系统、UI控件、订单明细),硬套继承容易导致类型爆炸——Folder 继承 File?CompositeNode 继承 LeafNode?逻辑上就别扭。
- 定义统一接口 Component,声明
add()、remove()、operation() -
Leaf 实现
operation(),空实现add()/remove() -
Composite 持有 Component 列表,
operation()遍历委托给子项 - 客户端无需判断类型,对单个对象或整个子树调用同一方法
组合不是放弃继承,而是分层使用
组合和继承不互斥。合理做法是:用继承表达“是什么”(is-a),用组合表达“有什么”(has-a)或“能做什么”(can-do)。
- 例如:Duck 是一种 Animal(继承),但它 has 一个 FlyBehavior 和一个 QuackBehavior(组合)
- 基类保留核心身份和生命周期逻辑(如
live()、die()),行为细节交给组合对象 - 避免多层继承链(A→B→C→D),把中间层的“行为差异”抽成策略类,由顶层类组合
警惕组合带来的间接性陷阱
组合增强灵活性的同时,可能增加调用链长度和理解成本。需要主动控制复杂度。
- 组件接口要小而专注,避免一个 FeatureManager 扛下十个职责
- 提供便捷构造方式,比如 Builder 或工厂方法,隐藏繁琐的组装过程
- 在关键路径上做性能评估——频繁代理调用是否成为瓶颈?必要时可缓存或内联
- 用命名和文档明确组合契约,例如 “Renderer 必须在 update() 后调用 render()”
不复杂但容易忽略:真正让组合落地的,不是语法,而是设计时多问一句——这个能力,是它“本来就是”,还是“可以配上”?答案指向哪里,架构就该往哪里倾斜。

















