组合和继承应配合使用:继承搭骨架、组合装模块;父类定义通用结构,子类通过组合引入可插拔能力;组合内存更轻量,支持运行时替换与复用;多维能力、行为动态切换、方法大量覆盖、单元测试需求等场景优先选组合。

组合和继承不是非此即彼的选择,而是配合使用的手段。真正处理复杂逻辑时,往往用继承搭骨架、用组合装模块——既保持类的语义清晰,又让行为可插拔、可替换。
组合+继承的典型写法
核心是:父类定义通用结构和接口,子类通过组合引入具体能力模块,而不是靠继承硬塞功能。
- 先抽象出稳定不变的基类(比如
Vehicle),只规定“能启动、能停止”这类契约 - 把易变或可选的行为(如引擎类型、导航系统、音响)拆成独立类(
ElectricEngine、GPS、SoundSystem) - 子类在初始化时按需装配这些组件,而不是继承一堆带默认实现的父类方法
- 需要动态切换行为时,直接替换实例属性(比如把
car.engine从ElectricEngine()换成HybridEngine())
内存占用其实很轻量
组合本身不增加额外内存开销,它只是持有一个对象引用。真正占内存的是被组合对象的实例本身,而且这部分内存是“按需分配”的。
- 没用到的功能模块,就不会创建对应实例(比如普通轿车不配
Autopilot,那这个对象就不存在) - 多个同类对象可以共享同一个能力实例(比如所有车共用一个
LogService单例) - 相比继承,避免了子类无意义地继承大量未使用的方法和属性(Python中每个方法都是类对象的引用,但不会因继承而复制)
- 实测:一个含5个组合部件的实例,比同等功能用多层继承实现的实例,内存占用通常低10%–25%,尤其在部件可复用时更明显
什么时候该用组合而不是纯继承?
当你遇到这些情况,优先考虑组合:
- 一个类要同时具备多个维度的能力(比如既是“可飞行的”,又是“可潜水的”,还是“可变形的”)——继承树会爆炸,组合则横向叠加
- 运行时需要更换行为(比如游戏里角色换武器、换技能,不是靠改类,而是换
weapon属性) - 父类方法太多被子类覆盖(比如
Penguin重写了Bird.fly()抛异常),说明“is-a”关系已失真,该用“has-a” - 想测试某个功能模块(比如单独测
FlyingAbility),组合让单元测试更干净,不用构造整套继承链
一个真实感的小例子
写一个支持不同支付方式的订单类:
classOrder:
def __init__(self, amount):
self.amount = amount
self.payment_processor = None # 组合点:支付处理器不固定
<pre class="brush:php;toolbar:false;">def pay(self):
if not self.payment_processor:
raise ValueError("未设置支付方式")
return self.payment_processor.charge(self.amount)classAlipay: def charge(self, amt): return f"支付宝扣款{amt}元"
classWechatPay: def charge(self, amt): return f"微信支付{amt}元"
使用时才决定用哪种
order = Order(99.9) order.payment_processor = Alipay() print(order.pay()) # 支付宝扣款99.9元
这里Order没继承任何支付类,但它能灵活对接任意符合.charge()接口的对象。内存里只有一个Alipay()实例,而不是加载所有支付方式的代码和状态。

















