为什么更喜欢组合而不是继承?每种方法都有哪些权衡?什么时候应该选择继承而不是组合?
当前回答
公正的观点是,继承只应在以下情况下使用:
两个类都在同一逻辑域中子类是超类的适当子类型超类的实现对于子类是必要的或适当的子类所做的增强主要是附加的。
有时,所有这些东西都会汇聚在一起:
更高级别的域建模框架和框架扩展差分编程
其他回答
我听过的一条经验法则是,当它是一种“is-A”关系时,应该使用继承;当它是“has-A”关系的时候,应该使用组合。尽管如此,我觉得你应该始终倾向于构图,因为它消除了很多复杂性。
继承是非常诱人的,尤其是来自程序领域的继承,它通常看起来很优雅。我的意思是,我需要做的就是将这一点功能添加到其他类中,对吗?嗯,问题之一是继承可能是最糟糕的耦合形式
基类通过以受保护成员的形式向子类公开实现细节来打破封装。这会使您的系统变得僵化和脆弱。然而,更悲惨的缺陷是新的子类带来了继承链的所有包袱和观点。
《继承是邪恶的:DataAnnotationsModelBinder的史诗般的失败》一文在C#中讲述了一个这样的例子。它显示了继承在何时应该使用组合以及如何重构组合。
公正的观点是,继承只应在以下情况下使用:
两个类都在同一逻辑域中子类是超类的适当子类型超类的实现对于子类是必要的或适当的子类所做的增强主要是附加的。
有时,所有这些东西都会汇聚在一起:
更高级别的域建模框架和框架扩展差分编程
除了是/有一个考虑因素之外,还必须考虑对象必须经历的继承的“深度”。任何超过五或六级继承深度的内容都可能会导致意外的强制转换和装箱/拆箱问题,在这种情况下,编写对象可能是明智的。
更喜欢组合而不是继承,因为它更具延展性/更易于稍后修改,但不要使用组合始终方法。通过组合,使用依赖注入/设置器可以很容易地动态改变行为。继承更为严格,因为大多数语言不允许从多个类型派生。因此,一旦你从A型衍生出,鹅或多或少都是熟的。
我的上述酸性测试是:
TypeB是否希望公开TypeA的完整接口(所有公共方法都不少于),以便在需要TypeA的地方使用TypeB?表示继承。例如,赛斯纳双翼飞机将暴露出飞机的完整界面,如果不是更多的话。因此,它适合于从飞机衍生。TypeB是否只想要TypeA暴露的部分行为?表示需要合成。例如,鸟可能只需要飞机的飞行行为。在这种情况下,将其作为接口/类/两者提取出来并使其成为两个类的成员是有意义的。
更新:刚刚回到我的答案,现在看来,如果没有具体提到芭芭拉·里斯科夫的里斯科夫替代原则作为“我应该继承这种类型吗?”的测试,它似乎是不完整的