59

优先组合而不是继承

是很流行的一句话。我读了几篇文章,最后每篇文章都说

当类之间存在纯 IS-A 关系时使用继承。

这篇文章的一个例子:

这里AppleFruit之间有明确的 IS-A 关系,即 Apple IS-A Fruit,但作者也将其显示为 Apple HAS-A Fruit(组合)以显示使用继承实现时的陷阱。

我在这里变得有些困惑,声明的含义是什么

当类之间存在纯 IS-A 关系时使用继承。

使用组合而不是继承是否意味着即使存在纯 IS-A 关系也始终尝试应用组合 ,并且仅在组合没有意义的情况下保留继承?

4

7 回答 7

90

当您使用继承来重用来自超类的代码,而不是重写方法并定义另一个多态行为时,这通常表明您应该使用组合而不是继承。

该类java.util.Properties是继承使用不当的一个很好的例子。它不是使用Hashtable 来存储其属性,而是扩展Hashtable,以便重用其方法并避免使用委托重新实现其中的一些方法。

于 2012-07-05T12:25:21.197 回答
7

我认为这是面向对象设计中讨论最多的一点之一。正如文章中所建议的,组合总是优于继承。这并不意味着你永远不应该使用继承。你应该在更有意义的地方(这值得商榷)。

使用组合有很多优点,其中几个是:

  • 您将完全控制您的实施。即,您只能公开您打算公开的方法。
  • 超类中的任何更改都可以通过仅在您的类中进行修改来屏蔽。任何使用您的类的客户端类都不需要进行修改。
  • 允许您控制何时加载超类(延迟加载)
于 2012-07-05T12:26:21.097 回答
4

我想一个好的指导方针是:

当存在 IS-A 关系时,使用继承。否则,使用组合。

其原因与面向对象设计中的另一个概念——多态性有关。多态性是许多 OOP 语言的一个特性,其中一个对象可以用来代替另一个对象,前提是第一个对象的类是第二个对象的子类。

为了说明,想象一下如果你有一个函数可以接收一种动物。当你使用继承时,你只有一个功能:

void feed( Animal a );

多态性向我们保证,我们放入的任何动物子类都会被接受。否则,我们将被迫为每种类型编写一个函数。我认为这样做的好处超过了它的缺点(例如减少封装)。

如果没有 IS-A 关系,我认为多态不会很有效。因此,最好使用组合并增强类之间的封装/隔离。

于 2012-07-06T13:57:00.420 回答
3

在文章中,没有这样的短语:

use inheritance when there is pure IS-A relationship between classes

此外,除了您的帖子之外,谷歌没有在其他地方找到它。

相反,文章写道:

Make sure inheritance models the is-a relationship. My main guiding philosophy is that inheritance should be used only when a subclass is-a superclass. In the example above, an Apple likely is-a Fruit, so I would be inclined to use inheritance.

这意味着,如果存在 IS_A 关系,请尝试先使用继承,而不是组合。并且没有偏好using composition over inheritance- 每个都适合自己的角色。

于 2012-07-05T12:31:38.217 回答
3

另外,我想补充一点,装饰器模式是一个示例,您可以在其中使用组合而不是继承,并动态地向对象添加职责。装饰器模式的示例在 Effective Java 中的“Favor Composition over Inheritance”项中提到。以 Java API 为例;BufferedReader 装饰了一个 Reader(而不是扩展 FileReader、PipedReader、FilterReader 等),因此可以装饰任何类型的 Reader。缓冲功能在运行时附加到这些阅读器,这是装饰器模式。

这里通过子类化扩展是不切实际的。

于 2012-07-05T13:20:45.797 回答
2

当关系是永久的时使用继承。不要仅仅为了获得自由行为而使用扩展。这就是他们在 IS-A 示例中所指的内容。仅当扩展类确实是父类的扩展时才扩展。有时它不会被切割和干燥,但一般来说。如果您需要从“正确的”类扩展,组合在未来也提供了灵活性。

这是一篇关于扩展的古老但很棒的文章:

http://www.javaworld.com/javaworld/jw-08-2003/jw-0801-toolbox.html

于 2012-07-05T12:29:48.953 回答
0

我会说不。在水果/苹果场景中,使用继承是很有意义的。这篇文章的作者也证实了这一点:“在上面的例子中,苹果很可能是水果,所以我倾向于使用继承”。

如果您的子类显然是超类,那么继承就可以了。但在实践中,您会发现更多使用组合可以更好地解决的情况。

于 2012-07-05T12:19:29.843 回答