【问题标题】:Why there is an option for a class in OOP be marked to never inherit from ancestral?为什么 OOP 中的一个类有一个选项被标记为从不继承祖先?
【发布时间】:2012-09-17 06:40:14
【问题描述】:

C# 中的“sealed”关键字,Java 中的 Final。

由于我几乎从不创建任何图表,而且我只使用已经完成的类(来自框架),所以多年后我仍然不知道为什么有人会“锁定”一个类,因此它永远不会被扩展/继承。

有用吗? 是否有任何危害使所有类都可以从继承中扩展而放弃“密封”的可能性?

很抱歉在 2012 年问这个问题,OOP 在哪里微不足道,但我希望得到一个很好的解释和/或一个很好的阅读来源!因为对我来说毫无用处,我不敢相信这只是一个简单的概念。

但无论我在哪里搜索,答案都是一样的:“标记一个类,防止它被其他人继承。”。

【问题讨论】:

  • 最好的例子,为什么有人想从这个类继承class ConsoleApplication12345 { public static void main() { Console.Writeline("Hello World"); } }
  • 说实话,我从来没有尝试过继承主类!我会检查一下。 QQ

标签: oop inheritance


【解决方案1】:
  1. 安全性 - 继承类可能有权访问基类的内部部分,从而破坏封装

  2. 保留合同 - 继承类可能会破坏基类提供的合同。参见 Liskov 替换原则

  3. 不可变性 - 2 的特例。 - 继承类可以将可变字段引入不可变类

  4. 性能 - 虚拟机可以积极优化此类类,例如假设方法永远不会被覆盖,避免虚拟调用

  5. 简单性 - 实现像equals() 这样的方法要简单得多,无需担心继承

【讨论】:

  • 谢谢,我不知道这 5 件事。说得通。我想了解更多有关 OOP 的详细信息。
【解决方案2】:

原因是,如果您允许子类化,并且对子类可以覆盖的方法没有任何限制,那么对保留 行为的基类进行更新仍然会破坏子类。换句话说,从不是专门设计为扩展的类继承是不安全的(通过将特定方法指定为可覆盖并使所有其他方法final。)这称为脆弱基类问题 - 请参阅this paper举个例子,this paper 对问题进行更全面的分析。

如果基类不是为继承而设计的,并且它是公共 API(可能是库)的一部分,那么您现在就遇到了严重的麻烦。您无法控制谁对您的代码进行子类化,并且您无法仅通过查看基类就知道更改对子类是否安全对子类底线是您要么设计为无限继承,要么完全禁止它。


请注意,可能有有限 个子类。这可以通过私有构造函数和内部类来实现:

class Base {
    private Base() {}

    (public|private) static final class SubA extends Base { ... }
    (public|private) static final class SubB extends Base { ... }
    (public|private) static final class SubC extends Base { ... }
}

内部类可以访问私有构造函数,但顶级类没有,因此您只能从内部对其进行子类化。这允许您表达“Base 类型的值可以是 SubA OR SubB OR SubC”的概念。这里没有危险,因为 Base 通常是空的(您并没有真正从 Base 继承任何东西)并且所有子类都在您的控制之下。

您可能会认为这是代码异味,因为您知道子类的类型。但是您应该意识到,这种实现抽象的替代方式是对接口的补充。当子类数量有限时,添加新函数很容易(处理固定数量的子类),但很难添加新类型(每个现有方法都需要更新以处理新子类)。当您使用接口或允许无限子类化时,添加新类型很容易(实现固定数量的方法),但很难添加新方法(您需要更新每个类)。一个人的优点就是另一个人的缺点。有关该主题的更详细讨论,请参阅On Understanding Data Abstraction, Revisited编辑:我的错误;我链接的论文讨论了不同的二元性(ADT 与对象/接口)。我真正想到的是Expression Problem


避免继承的原因也不那么严重。假设您从 A 类开始,然后在子类 B 中实现一些新功能,然后在子类 C 中添加另一个功能。然后您意识到您需要这两个功能,但是您不能创建一个扩展 B 和 C 的子类 BC。即使您设计继承并防止脆弱的基类问题,也可能会被此咬伤。除了我上面展示的模式之外,most uses of inheritance are better replaced with composition - 例如,使用 Strategy Pattern(或者如果你的语言支持,则简单地使用高阶函数。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-06
    • 1970-01-01
    • 1970-01-01
    • 2017-03-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多