为什么更喜欢组合而不是继承?每种方法都有哪些权衡?什么时候应该选择继承而不是组合?
当前回答
当您想要“复制”/公开基类的API时,可以使用继承。当您只想“复制”功能时,请使用委派。
其中的一个例子是:您想要从列表中创建堆栈。堆栈只有pop、push和peek。考虑到堆栈中不需要push_back、push_front、removeAt等功能,您不应该使用继承。
其他回答
合成与继承是一个广泛的主题。对于什么更好,没有真正的答案,因为我认为这一切都取决于系统的设计。
通常,对象之间的关系类型为选择其中一个对象提供了更好的信息。
如果关系类型是“is-A”关系,则继承是更好的方法。否则关系类型为“HAS-A”关系,则组合将更接近。
这完全取决于实体关系。
继承带来了所有不可否认的好处,下面是它的一些缺点。
继承的缺点:
您不能在运行时更改从超级类继承的实现(显然是因为继承是在编译时定义的)。继承将子类暴露给其父类实现的细节,这就是为什么人们常说继承破坏了封装(从某种意义上讲,您真正需要关注的是接口而不是实现,所以通过子类重用并不总是首选的)。继承提供的紧密耦合使得子类的实现与超级类的实现紧密结合,父类实现中的任何更改都将迫使子类进行更改。子类的过度重用会使继承堆栈变得很深,也很混乱。
另一方面,通过对象获取对其他对象的引用,在运行时定义对象组合。在这种情况下,这些对象将永远无法访问彼此的受保护数据(没有封装中断),并将被迫尊重彼此的接口。在这种情况下,实现依赖性也会比继承的情况下少得多。
简而言之,我同意“比起继承,我更喜欢成分”,但对我来说,这听起来常常像“比起可口可乐,我更爱土豆”。有继承的地方,也有合成的地方。你需要了解差异,然后这个问题就会消失。这对我来说真正的意义是“如果你要使用继承——再想想,你很可能需要复合”。
当你想吃的时候,你应该更喜欢土豆而不是可口可乐,当你想喝的时候,应该更喜欢可口可乐而不是土豆。
创建子类不仅仅意味着调用超类方法的便捷方式。当子类在结构上和功能上都是一个超级类时,您应该使用继承,因为它可以用作超级类,并且您将使用它。如果不是这样的话,那不是继承,而是其他东西。合成是指你的对象由另一个组成,或者与它们有某种关系。
所以对我来说,如果有人不知道自己是否需要继承或组成,真正的问题是他不知道自己是想喝酒还是想吃饭。多想想你的问题领域,更好地理解它。
这条规则完全是无稽之谈。为什么?
原因是,在任何情况下,都可以区分是使用组合还是继承。这取决于对一个问题的回答:“是什么?是什么?”或“有什么?”其他的东西“。
你不能“宁愿”把某样东西变成另一样东西或拥有另一样。严格的逻辑规则适用。
也没有“人为的例子”,因为在任何情况下都可以给出这个问题的答案。
如果你不能回答这个问题,还有其他问题。这包括类的责任重叠,这通常是错误使用接口的结果,较少是通过在不同类中重写相同的代码。
为了避免这种情况,我还建议为类使用与它们的职责完全相似的好名称。
我同意@帕维尔的说法,他说,有地方可以组成,也有地方可以继承。
我认为如果你的答案是肯定的,那么应该使用继承。
您的类是从多态性中受益的结构的一部分吗?例如,如果您有一个Shape类,它声明了一个名为draw()的方法,那么我们显然需要Circle和Square类作为Shape的子类,这样它们的客户端类将依赖于Shape而不是特定的子类。您的类是否需要重用其他类中定义的任何高级交互?如果没有继承,模板方法设计模式将不可能实现。我相信所有可扩展框架都使用这种模式。
然而,如果您的意图纯粹是代码重用,那么组合很可能是更好的设计选择。