为什么更喜欢组合而不是继承?每种方法都有哪些权衡?什么时候应该选择继承而不是组合?


当前回答

这条规则完全是无稽之谈。为什么?

原因是,在任何情况下,都可以区分是使用组合还是继承。这取决于对一个问题的回答:“是什么?是什么?”或“有什么?”其他的东西“。

你不能“宁愿”把某样东西变成另一样东西或拥有另一样。严格的逻辑规则适用。

也没有“人为的例子”,因为在任何情况下都可以给出这个问题的答案。

如果你不能回答这个问题,还有其他问题。这包括类的责任重叠,这通常是错误使用接口的结果,较少是通过在不同类中重写相同的代码。

为了避免这种情况,我还建议为类使用与它们的职责完全相似的好名称。

其他回答

我的经验法则是:在使用继承之前,考虑组合是否更有意义。

原因:子类化通常意味着更复杂和更紧密,即更难在不出错的情况下进行更改、维护和扩展。

Sun的Tim Boudreau给出了一个更完整、更具体的答案:

在我看来,使用继承的常见问题是:无辜的行为可能会产生意想不到的结果——这方面的经典例子是调用超类中的可重写方法构造函数,在子类实例字段初始化。在一个完美的世界里,没有人会这样做。这是不是一个完美的世界。它给子类提供了不正当的诱惑,让它们对方法调用的顺序等做出假设——这种假设往往不会如果超类可以随着时间的推移而发展,那么它是稳定的。另请参见我的烤面包机和咖啡壶类比。类变得更重了——你不一定知道你的超类在其构造函数中做了什么工作,或者它需要多少内存使用。因此,构建一些无辜的轻量级对象可以比你想象的要贵得多,如果超类进化它鼓励子类的爆炸。类加载需要时间,更多的类需要内存。在你与NetBeans规模的应用程序打交道,但在那里,我们有了真正的例如,由于第一次显示菜单触发了大量的类加载。我们通过移动到更多的声明性语法和其他技术,但这需要花费时间修复。这使得以后改变事情变得更加困难——如果你公开了一个类,那么交换超类会破坏子类-这是一个选择,一旦你公开了代码,你就结婚了所以如果你不改变你的真实功能超类,如果你使用,而不是扩展你需要的东西。例如,子类化JPanel-这通常是错误的;如果子类是在公共场所,你永远不会有机会重新审视这个决定。如果它作为JComponent getThePanel()访问,您仍然可以这样做(提示:将组件的模型作为API公开)。对象层次结构不可缩放(或使其稍后缩放比提前规划要困难得多)-这是经典的“太多层”问题我将在下面讨论AskTheOracle模式如何解决它(尽管它可能冒犯OOP纯粹主义者)。...如果你允许继承遗产,我会怎么做带着一粒盐是:永远不显示字段,常量除外方法应为抽象方法或最终方法不从超类构造函数调用任何方法...所有这些都不适用于小型项目,而适用于大型项目私人课程比公共课程

另一个非常实用的原因是,倾向于组合而不是继承,这与域模型以及将其映射到关系数据库有关。将继承映射到SQL模型真的很难(你最终会遇到各种棘手的变通方法,比如创建不经常使用的列、使用视图等)。一些ORML试图处理这个问题,但它总是很快变得复杂。通过两个表之间的外键关系可以很容易地对组合进行建模,但继承要困难得多。

我听过的一条经验法则是,当它是一种“is-A”关系时,应该使用继承;当它是“has-A”关系的时候,应该使用组合。尽管如此,我觉得你应该始终倾向于构图,因为它消除了很多复杂性。

我看没有人提到钻石问题,这可能是继承带来的。

总之,如果类B和C继承了a,并且都重写了方法X,而第四个类D继承了B和C,并且没有重写X,那么应该使用X D的哪个实现?

维基百科对这个问题中讨论的主题提供了一个很好的概述。

当您想要“复制”/公开基类的API时,可以使用继承。当您只想“复制”功能时,请使用委派。

其中的一个例子是:您想要从列表中创建堆栈。堆栈只有pop、push和peek。考虑到堆栈中不需要push_back、push_front、removeAt等功能,您不应该使用继承。