我相信“优先选择组合而不是继承”的著名建议是在《GoF 设计模式》一书中提出的。
\n\n它说(第 20 页):
\n\n\n\n\n优先考虑对象组合而不是类继承。
\n\n理想情况下,您不必创建新组件来实现重用。您应该能够通过对象组合组装现有组件来获得所需的所有功能。但这种情况很少发生,因为在实践中可用组件集永远不够丰富。通过继承重用可以更轻松地创建可与旧组件组合的新组件。因此,继承和对象组合一起工作。
\n\n然而,我们的经验是,设计者过度使用继承作为重用技术,并且通过更多地依赖对象组合,设计通常变得更加可重用(并且更简单)。您将看到在设计模式中一次又一次应用对象组合。
\n
请注意,此语句指的是类继承,并且必须与接口继承区分开来,这很好。
\n\n活力
\n\n两者都是实现可重用性的方法,但组合相对于继承的优势在于动态性。由于组合可以在运行时动态更改,因此这是一个很大的优势,而继承是在编译时静态定义的。
\n\n封装
\n\n此外,组合基于使用组合对象的公共接口,因此对象尊重彼此的公共接口,从而促进封装。另一方面,继承破坏了封装,因为子组件通常使用来自父组件的受保护接口。父类的改变会破坏子类,这是一个众所周知的问题,即著名的基类问题。此外,在继承中,父类定义了子类的物理表示,因此子类依赖于父类来发展。
\n\n凝聚
\n\n作文的另一个优点是它可以让课堂专注于一项任务,这也可以增强凝聚力。
\n\n负债
\n\n显然,组合的一个问题是您将拥有更多的对象和更少的类。这使得可视化您的设计及其如何实现其目标变得更加困难。调试代码时,除非您知道对象当前正在使用给定组合的确切实例,否则很难知道发生了什么。因此,在我看来,构图让设计变得更难理解。
\n\n由于组合的优点是多方面的,这就是为什么建议使用组合而不是继承,但这并不意味着继承总是不好的。当继承被正确使用时,你可以取得很大的成就。
\n\n有趣的参考资料
\n\n我建议研究GoF 设计模式,以了解两种可重用性的好例子,例如使用组合的策略模式与使用继承的模板方法。
\n\n大多数模式都充分利用接口继承,然后使用对象组合来实现其目标,只有少数模式使用类继承作为可重用性机制。
\n\n如果你想深入研究Holub on Patterns这本书,第 2 章有一个名为 Why extendsis Evil 的部分,该部分深入研究了类继承的责任。
书中具体提到了三个方面
\n\n\n\n\n
\n- 失去灵活性:第一个问题是显式使用具体类名称会将您锁定在特定的实现中,从而使后续更改变得不必要的困难。
\n- 耦合:实现继承的一个更重要的问题是耦合,即程序的一个部分对另一部分的不良依赖。全局变量是强耦合不好的典型例子。例如,如果更改全局变量的类型,则使用该变量\xe2\x80\x94(耦合到变量\xe2\x80\x94)的所有代码都会受到影响,因此所有这些代码都必须\n 检查、修改并重新测试。此外,所有使用该变量的方法都通过该变量相互耦合。也就是说,一种方法可能会通过在不恰当的时间更改变量 xe2x80x99s 的值来错误地影响另一种方法的行为。这个问题在多线程程序中尤其可怕。
\n- 脆弱基类问题:在实现继承系统(使用扩展的系统)中,派生类与基类紧密耦合,而这种紧密联系是不受欢迎的。设计者已应用名称 \xe2\ x80\x9c脆弱基类问题\xe2\x80\x9d来描述此行为。基类被视为 \xe2\x80\x9cfragile\xe2\x80\x9d 因为\n 您可以以看似安全的方式修改基类,但这种新的\n 行为在由派生类继承时可能会导致派生类\n n 类故障。
\n