组合模式通过“拥有”关系动态组装组件,避免继承导致的类爆炸与初始化死锁;组件职责单一、接口明确,主类委托调用,支持运行时灵活扩展与替换。

组合模式(Composition)通过“拥有”而非“是”来构建对象关系,能有效避免继承带来的类爆炸和死锁风险。关键在于把行为拆解为可插拔的组件,让类在运行时动态组合功能,而不是在编译期硬编码继承链。
用组件替代父类职责,抑制类爆炸
继承容易导致子类数量随功能维度指数增长(如:Window × Scrollable × Resizable × Borderless → 2⁴=16个类)。组合则把每个能力封装为独立组件:
-
ScrollableBehavior:负责滚动逻辑,含
scrollTo()、getScrollPosition() -
ResizableBehavior:管理尺寸变更,含
resizeTo()、onResize()回调 - BorderRenderer:只管边框绘制,不关心窗口是否可滚动
主类(如 Window)仅持有一组组件引用,并委托调用。新增功能只需新增组件类+注入,无需修改现有类或派生新子类。
打破继承链依赖,规避初始化死锁
多重继承或深层继承中,父类构造器常需调用虚方法,而子类字段尚未初始化,易引发空指针或状态不一致;若组件间存在循环依赖(如 A 初始化需 B,B 初始化又依赖 A),也会触发死锁。组合模式将依赖显式化并延迟到运行时:
- 组件实例由外部容器(如工厂或 DI 容器)统一创建和装配,顺序可控
- 组件之间通过接口通信,避免直接持有对方强引用;必要时用弱引用或回调注册代替构造期传入
- 初始化分阶段:先构造所有组件,再调用
setupDependencies()或bind()方法建立协作关系
用策略+委托实现行为复用,不靠继承树
传统继承复用代码常导致“胖基类”——基类塞满所有可能用到的方法,子类被迫继承无用逻辑。组合鼓励按角色分离:
- 定义
InputHandler接口,不同组件(KeyboardHandler、TouchHandler)实现它 -
Window持有InputHandler引用,运行时切换(如触屏设备用TouchHandler,桌面用KeyboardHandler) - 需要组合多个输入源?用
CompositeInputHandler聚合多个实现,自身仍不继承任何具体 handler 类
警惕组合滥用:边界与生命周期要清晰
组合不是万能解药。若组件职责模糊、边界不清,反而增加协调成本。注意三点:
- 每个组件应有单一明确职责,且对外暴露最小必要接口
- 主类需明确定义组件生命周期:谁创建、谁销毁、是否共享、是否线程安全
- 避免“组合嵌套过深”,例如
A → B → C → D的长委托链,此时应考虑合并相邻组件或引入事件总线解耦










