“组合优于继承”是因为继承是编译期就固定的强耦合:子类依赖父类的实现细节,父类一改可能波及所有子类(脆弱的基类问题);继承层级深了以后行为分散在各层,难以理解和修改;而且一个类只能有一个父类,维度一多(如“会飞的”“会游泳的”)类数量会组合爆炸。

组合则是运行期的松耦合:对象持有实现某接口的协作者,行为靠委托拼装,可以按需替换、按需测试,每个组件职责单一。

但“优于”不等于“取代”。当确实存在稳定的 is-a 关系、且子类与父类契约清晰(符合里氏替换)时,浅层继承很合理,模板方法模式就是典型用法。判断口诀:is-a 用继承,has-a / 行为复用用组合。

// 组合:按能力拼装,而非继承一个“会叫的动物”基类
class Duck {
    private final FlyBehavior fly;
    Duck(FlyBehavior fly) { this.fly = fly; }
}

追问方向:继承破坏封装的具体机制、mixins/trait 算不算继承的改良。