6

最近有人告诉我,在面向对象编程中的一个很好的做法是,您应该始终允许从您的类中继承。我真的不这么认为,但我没有可靠的论据。

阻塞继承的真实例子:

  1. 没有 C++ STL 类(专用类模板)允许继承(具有非虚拟析构函数)。
  2. Java 具有final适用于许多标准组件的类修饰符,例如java.lang.String.

我认为可能的原因是:

  1. 安全性,因为子类可能有权访问敏感的内部。(我不这么认为——他们不会访问私人成员。)
  2. Performance,因为子类可能会通过覆盖一些成员函数来破坏我们的高效实现。(孩子不会覆盖非虚拟功能。)
  3. 强制组合而不是继承。(我完全同意。我们不应该支持不需要的继承。)

所以我的问题是:在什么情况下我应该故意阻止继承?

4

3 回答 3

4

对于初学者来说,只有在您确定不希望其他人扩展您的类时才不允许继承。通常不建议出于琐碎的原因(例如性能)而阻止继承,因为代码重用通常超过通过标记 class 可以获得的小幅性能提升final

话虽如此,这里有几个您可能想要显式阻止继承的示例:

  1. 您正在编写一个商业的、封闭源代码的类,并且您不希望人们能够更改功能。这是阻止类继承的一个很好的理由,因为如果人们重写了你的方法和/或扩展了你的类,并且抱怨他们得到了意想不到的结果,你不希望以后必须支持它。

  2. 您正在设计一个不可变的类。通过标记 class final,您可以防止子类损害您的类的不可变行为。例如,如果您被允许对 s 进行子类化String,那么其他人可以制作自己的实现来允许String修改 s。现在没有任何代码String可以确定对象是不可变的。

  3. 您想强制组合而不是继承。当您想要避免类之间的紧密耦合时,这是可取的(即,您不想要高度依赖于彼此的类组)。

  4. 您想鼓励编译器进行内联。将类和方法标记为 final 可能会带来很小的性能提升,因为它将确保 Java 不必在运行时查找正确的类方法来调用对象。非最终方法被标记为虚拟,以便在需要时可以适当地扩展它们,最终方法可以直接链接或在类中内联编译。请注意,您可以通过这样做获得的性能提升通常是微不足道的(特别是如果您的类的方法很大)。

于 2012-05-05T18:39:12.333 回答
4

事实上,我试图遵循的做法,以及 Josh Bloch 在他的Effective Java书中推荐的做法,正是你被告知的那个相反的规则:除非你考虑过继承,否则将你的类设计为被继承,并记录了您的类必须如何被继承,您应该始终禁用继承。

我建议阅读 Effective Java 的这一章(你不会后悔购买它),并将它展示给告诉你这条规则的人。

不允许继承的最明显原因是不可变性。不可变对象易于使用(只有一种状态),可以被缓存,在许多对象之间共享,并且本质上是线程安全的。如果类是可继承的,那么任何人都可以扩展该类并通过添加可变属性使其可变。

于 2012-05-05T17:52:56.693 回答
3

这只是我的0.02...

允许类继承允许人们处理不可预见的问题。(例如,经常出现在 RoR 中的猴子补丁。它可能很丑陋,但它是现实与迂腐)。话虽如此,我并不是无偿继承的忠实拥护者。基类和子类之间的关系可能很脆弱。深层次的继承层次很难理解。

我能想到的不允许继承的一种情况是强制不变性。这对于 Java String 类之类的东西很重要。

于 2012-05-05T17:53:39.807 回答