本问题已经有最佳答案,请猛点这里访问。
Favor composition over inheritance
是非常流行的短语。我读了几篇文章,最后每篇文章都说
use inheritance when there is pure IS-A relationship between classes.
本文的一个示例:
苹果和水果之间存在着明显的"是"关系,即苹果是一种水果,但作者也将其表示为"苹果有"——一种水果(成分),用继承来实现时会显示出陷阱。
我在这里有些困惑,陈述的意思是什么
use inheritance when there is pure IS-A relationship between classes.
在继承上使用组合是否意味着即使存在纯IS-A关系,也要始终尝试应用组合,并且只在组合没有意义的情况下保留继承?
为什么java标签?
当您使用继承重用来自超类的代码,而不是重写方法和定义另一个多态行为时,通常会指示您应该使用组合而不是继承。
java.util.Properties类是继承使用不当的一个很好的例子。它不是使用哈希表来存储其属性,而是扩展哈希表,以便重用其方法,并避免使用委托重新实现其中的一些方法。
我反对,因为这是一个不涉及汽车、动物或水果的真实例子。然而,如果说明扩展HashTable的java.util.Properties是如何对设计产生负面影响的,这将是一个更有力的论据。我考虑过里斯科夫替代原理,我认为这不是问题。从理论上讲,由于性能/内存的原因,能够更改存储(哈希)机制可能很有用,但这在继承和组合中是不可能的。
非重复性问题:是否不建议编写一个包含所有子类都具有完美意义的方法的基类(如果是抽象的则更好),而不重写任何基类的方法?扩展HashTable的Properties不是一个很好的例子,因为Properties不应该关心整个HashTable特定的逻辑,只需要其中的一小部分,所以它不应该扩展HashTable,因为它不是一个,它只是使用一个?
我认为这是面向对象设计中讨论最多的一点。正如本文所建议的,组合总是优先于继承。这并不意味着你不应该使用继承。你应该去更有意义的地方(有争议的地方)。
使用合成有很多优点,其中有两个优点:
您将完全控制您的实现。也就是说,您只能公开您打算公开的方法。
超级类中的任何更改都可以通过只在类中修改来屏蔽。任何使用类的客户机类都不需要进行修改。
允许您控制何时加载超级类(延迟加载)
我的问题是,我不介意转向更多的组合,但是对于具有许多方法的类,在每个派生类中重复这个样板文件可能会很痛苦。当然,大多数类应该只有几个方法,但在某些情况下(例如,GUI窗口基类),您会忍不住拥有很多方法。
我想一个好的指导方针是:
When there is an IS-A relationship, use inheritance. Otherwise, use
composition.
这与面向对象设计中的另一个概念——多态性有关。多态性是许多OOP语言的一个特性,只要第一个类是第二个类的子类,就可以用一个对象代替另一个对象。
举例来说,想象一下,如果你有一个功能,接受一种动物。使用继承时,只有一个函数:
void feed( Animal a );
多态性保证了我们放入的动物的任何亚类都将被接受。否则,我们将被迫为每种类型编写一个函数。我认为这样做的好处大于它的缺点(例如减少封装)。
如果没有IS-A关系,我认为多态性不会非常有效。因此,最好使用组合并增强类之间的封装/隔离。
If there is no IS-A relationship, I think polymorphism won't be very effective. It would thus be better to use composition and have enhanced encapsulation/isolation between classes.1+1
另外,我想补充一点,装饰器模式是一个示例,您可以在继承上使用组合,并动态地向对象添加职责。在"有效继承比继承"项目中,在有效Java中提到了装饰器模式的例子。从Java API中获取一个例子;Buffer阅读器装入阅读器(而不是扩展FieleRader、PiPadReader、FieldReader等),因此有能力对任何类型的阅读器进行装饰。缓冲功能在运行时附加到这些读卡器上,这就是装饰器模式。
在这里,通过子类化进行扩展是不切实际的。
在本文中,没有这样的短语:
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.
这意味着,如果存在关系,请首先尝试使用继承,而不是组合。而且,using composition over inheritance没有任何偏好——每个都有利于自己的角色。
each is good for its own role.1+1。我认为这意味着这取决于我们建立阶级关系的"动机"。如果它是"仅"用于代码重用,那么Favor composition over inheritance是有效的;如果其动机是模拟类之间实际存在的"实际关系",那么继承和组合都有自己的角色。如果存在纯粹的IS,我们可以选择继承——一种关系"尽管"事实上继承比组合有更多的问题,比如强耦合,否则选择组合。我说的对吗?
代码重用是一个强大的动机,仅仅选择继承就足够了。在许多情况下,强耦合被认为是有利的,而不是问题。在许多语言(Java)中,继承比作文更容易。只有经验才能让你感觉到什么是正确的方法,对于特定的情况,没有严格的规则。
当关系是永久的时使用继承。不要仅仅为了获得自由行为而使用扩展。这就是他们在IS上所指的——一个例子。只有当扩展类确实是父类的扩展时才进行扩展。有时它不会被切割和干燥,但一般来说。如果您需要从"正确的"类进行扩展,那么组合在将来也提供了灵活性。
这是一篇关于延伸的古老而伟大的文章:
http://www.javaworld.com/javaworld/jw-08-2003/jw-0801-toolbox.html
我会说不,在水果/苹果的场景中,使用继承是很有意义的。这篇文章的作者也证实了这一点:"在上面的例子中,苹果很可能是一种水果,所以我倾向于使用继承。"
如果您的子类显然是一个超类,那么继承就可以了。在实践中,你会发现更多的情况下,更好地解决使用组合。
"如果你的子类显然是超类"?
@SimonNickerson"如果显然是一个"