我们怎么知道偏爱合成而不是泛泛总是正确的选择?


9

无论对象是否物理存在,我们都可以选择以不同方式对其进行建模。在许多情况下,我们可以随意使用归纳或组合。但是,GoF的“偏爱组合胜于一般化”原则指导我们使用组合。因此,例如,当我们对一条线进行建模时,我们将创建一个包含两个类型为Point(组成)的PointA和PointB成员的类,而不是扩展Point(广义化)。这只是我们可以随意选择合成或继承进行建模的简化示例,尽管对象通常要复杂得多。

我们怎么知道这是正确的选择?至少很重要,因为如果做错了,可能要进行大量的重构?


9
您的示例并没有真正起作用,因为您不能说一条线是一个点,因此它使Liskov替换原理失效,并且继承不合适。
迪恩·哈丁

@Dean:您可能已经知道,该示例并不是重点。为了记录,一条线可以表示为通过两个点定义的方程。
CarneyCode


1
@Songo:看来您完全错了。
CarneyCode

是不是扩展专业化而不是泛化?
图兰斯·科尔多瓦

Answers:


17

这并不总是正确的选择。在大多数情况下,它是最有利的。当复合模型需要更改或扩展时,它具有更高的抵抗力,因为您可以更改组成而不必担心会无意影响其他类。

我们怎么知道呢?从经验,以及他人的经验。

我已经看到许多情况,从一个简单的概念开始就形成了庞大的类层次结构,这是您进行重大重构的地方。同时,我从未见过需要将组成结构重构为继承的情况。

但是我的轶事证据还不够。只要环顾互联网,就会有很多人有相同的经历。


我喜欢 投票。
CarneyCode

+1表示“我从未见过需要将合成结构重构为继承的情况。”
Kazark

7

如果您希望有一个超越“偏重于继承而不是继承”的更强的经验法则,那么我可能会提出这样的建议:

专门化对象的两种方法中的一种-继承和合成-仅当您需要让您的对象对于要专业化的基类是多态的(可替换的)时,才应使用继承。

但是,就像所有经验法则一样,一旦您理解了规则-那么您就可以随意违反规则了:)


0

我看不到组合和泛化是如何替代的,因此找不到引号
组成和继承是(实现专门化),并且可以说抽象和泛化是(实现模块化)。

您想要的是设计简单合理,这是两种本质上很容易衡量的品质。
在您的情况下,似乎要用两点组成一条线而不是对其进行扩展,因为您自然会用两点定义一条线,而不是通过扩展点的概念来定义。


更自然吗?一条线是两点多于一条单点延伸吗?
CarneyCode

2
我从未见过不包含由2+点组成的事实的线定义。即使使用直线方程,如果您在数字域中只有1个x值,那么它就是一个点,而不是直线。
钻机

0

当两个候选人都符合条件时,就会受到青睐。该问题中所述的问题在此帐户上失败,因为它只有组合选项。

“合成也可以使用概括”。这句话对我们有意义吗?如果不是这样,那么我们还没有准备好掌握“赞成”规则。

我们之所以偏爱“组合”,是因为“组合”比泛化提供了更多的扩展性/灵活性。这种扩展/灵活性主要是指运行时/动态灵活性(这是通过接口和组合的组合来实现的)。

好处不是立即可见的。要查看好处,您需要等待下一个意外更改请求。因此,与那些接受组合的人相比,大多数情况下坚持概括的人都失败了(后面提到的一个明显的情况除外)。因此,规则。从学习的角度来看,如果您可以成功实现依赖项注入,那么您应该知道哪一个以及何时支持。当您不确定要选择哪个时,该规则可帮助您做出决定。同样,您应该能够首先看到两个选项都对其中一个有利。

摘要:组成:通过将一些较小的东西插入更大的物体中,可以减少耦合,而较大的对象只是将较小的对象回调。泛化:从第三方API的角度来看,定义一种方法可以被重写比定义一种可以调用的方法(要确保泛化是成功的),它的承诺更强。而且永远不要忘记,对于合成,您也正在从接口而不是大型类中使用泛化。但是不幸的是,全部功劳都归功于组成。


-4

今天我写了大约300个LoC。而且我不记得任何我可能违反和不违反的原则。我希望,重构将拯救我的灵魂。

如果抽象是由3d方api决定的-通常使用继承是正确的。在国内设计中,实体必须是抽象的或密封的。抽象实体可能必须具有大约3个继承者。我不喜欢一种虚拟方法的扩展点。以上就是我的口味。我不是在谈论接口抽象,这是完全不同的故事。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.