为什么子类化太糟糕了(因此为什么我们应该使用原型来消除它)?


Answers:


19

为什么子类化太糟糕了

“太多”是一种判断。但是从我这里拿走是不好的。我使用的代码具有DEEP继承性,而且该代码简直难以阅读,理解,跟踪,跟踪,调试等。该代码实际上是不可能编写的。

原型模式消除了子类化

还是这个问题的意思是“放弃过多的子类化了吗?”。这种模式要求至少在那一点上克隆一个“模板”对象以避免子类化。没有规则说“模板”不能是子类。

偏重于继承而不是继承

这里的想法还包括委派和汇总。遵循这种启发式方法意味着您的软件趋向于更灵活,更易于维护,扩展和重用。

当类由部分组成时,可以在运行时替换这些部分。这对可测试性具有深远的影响。

测试更容易。您可以使用假零件(即“假装”,“双打”和其他测试用语)。我们代码的深层继承意味着我们必须实例化整个层次结构以对其进行任何测试。在我们的情况下,如果不在实际环境中运行代码,这是不可能的。例如,我们需要一个数据库来实例化业务对象。

变化会带来副作用和不确定性 -类别越“基础”,无论是好是坏,影响越广泛。由于副作用不确定,您可能不希望进行某些更改。否则对继承链中某个位置有利的更改对另一个不利。这当然是我的经验。


我真的很想看到一些例子,这些例子说明了继承何时使测试(尤其是模拟)变得困难,以及组合如何对此进行帮助。
Armand 2012年

@alison:派生类中的一种方法,它使用基类中的另一种方法,其中基类中的实现实际上使得无法测试错误方案。如果是合成的,注入会引起错误的对象会更容易
Rune FS

@allison:例子?当代码库很大时,直接由几十个类使用基类,而通过继承使用数百个基类。因为深度继承是重用,所以基类被泛化为调试器堆栈跟踪无法显示实际调用的方法。最后,当这样的科学怪人中没有内置依赖项注入时。顶级类可能“是”六个或更多其他事物,它们共同“拥有”许多内部对象-每个内部对象都有自己的继承乐趣。
Radarbob 2012年

8

我认为这被视为不良做法的原因之一是,随着时间的流逝,每个子类都可以包含属性和方法,而这些属性和方法对您而言越不合理(层次结构欠佳),则对该对象毫无意义。您最终可能会看到一个class肿的类,其中包含对该类没有意义的项目。

我自己对此并不了解。仅仅说这很糟糕似乎是教条。滥用任何东西都是不好的。


2

有两个原因。

  1. 是运行时性能。每次从超类调用子类时,您都在进行方法调用,因此,如果您嵌套了五个类并在基类中调用一个方法,则将进行五个方法调用。大多数编译器/运行时将尝试重新诊断并优化掉多余的调用,但是只有在非常简单的情况下才可以安全地这样做。

  2. 是您的程序员同伴的理智。如果您嵌套五个类,那么我必须检查所有四个父类,以确保您真的在基类中调用方法,这将变得很乏味。在调试程序时,我可能还必须逐步完成五个方法调用。


6
-1:您对#2的看法是正确的;但不是#1。继承的几层不一定会影响运行时性能。
Jim G.

担心在机器代码级别使用几个额外的过程调用以及使用OP和继承,并且您担心其他程序员的健全性……..
mattnz 2012年

@吉姆 可能不会,但是可以。:)还有可能要担心的内存使用情况:如果您不重用所有基本变量,则它们仍可能在构造对象时被分配。
亚当李尔

2

“太多”是一种判断。

与编程中的大多数事情一样,子类在合理的情况下并不是天生就不好如果您的问题解决方案模型更适合作为层次结构而不是组成部分,则子类化可能是首选。

话虽如此,主张继承而不是继承是一个很好的经验法则。

子类化时,测试可能会更加困难(如radarbob所述)。在深层次结构中,隔离有问题的代码更加困难,因为实例化子类需要引入整个超类层次结构和一堆附加代码。使用组合方法,您可以使用单元测试,模拟对象等隔离和测试聚合对象的每个组件。

子类化还可能导致您暴露您可能不需要的实现细节。这使得难以控制对数据的基本表示的访问,并且使您支持模型的特定实现联系在一起。

我能想到的一个示例是java.util.Properties,它继承自Hashtable。Properties类仅用于存储String-String对(这在Javadocs中有所说明)。由于继承的缘故,Hashtable的put和putAll方法已向Properties的用户公开。尽管存储属性的“正确”方法是使用setProperty(),但绝对没有阻止用户调用put()并传入String和Object的方法。

基本上,由于继承,属性的语义定义不明确。此外,如果有人想更改Properties的后备对象,则与使用Properties组合方法相比,对使用Properties的代码的影响将更大。


2

继承在某些情况下会破坏封装,如果确实发生,那就不好了。阅读更多。


在过去没有继承的过去,图书馆会发布API和使用它的应用程序来利用公开可见的内容,而图书馆现在拥有自己的方式来处理不断变化的内部机制而不会破坏App。

随着需求的发展,图书馆可以发展并拥有更多的客户。或者它可以通过从自己的类继承其他样式来提供扩展功能。到目前为止,一切都很好。

但是,最根本的是,如果一个应用程序继承了该类并开始对其进行子类化,那么现在一个子类实际上是一个客户而不是内部合作伙伴。但是,与明确定义公共API的明确方式不同,子类现在在库内部更深(访问所有私有变量等)。还算不错,但是讨厌的角色可以桥接API,而该API在APP以及库中太多了,

本质上,虽然并非总是如此,但是,

很容易滥用继承来破坏封装

如果这样的话,允许子类化可能是错误的。


1

简短的快速解答:

这取决于您在做什么。

扩展无聊的答案:

开发人员可以制作应用程序。使用子类化模​​板或(原型)模板。有时,一种技术效果更好。有时其他技术效果更好。

选择技术效果最好,也需要考虑“多重继承”方案是否存在。即使编程语言仅支持单一继承,模板还是原型。

补充说明:

一些答案与“多重继承”有关。替代方法(不是主要问题)与主题有关。关于“多重继承vs组合”,有几篇类似的文章。我有两种情况都混在一起的情况。

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.