设计继承

2006年10月6日

面向对象领域最长久的争论之一是关于开放继承和设计继承的辩论。设计继承的原则可能由Josh Bloch概括得最好:“为继承而设计并记录,否则禁止继承”。使用这种方法,你需要仔细决定哪些方法可以被继承,并密封其他方法以阻止它们被覆盖。

设计继承最常见的论点是,由于继承是一种非常亲密(并且破坏封装)的关系,子类很容易通过例如在调用方法时省略必要行为来有效地破坏其超类。

许多开发人员,特别是那些具有赋能态度的开发人员,发现这种论点不令人信服。我发现另一个更有说服力的论点是 Elliote Rusty Harold 在讨论XOM 的设计原则时提出的。这里的要点是“API 是由专家为非专家编写的”。库编写者应该精通库所使用的技术。她应该努力简化库用户使用的技术。封装就是关于隐藏秘密,因此一个好的库应该隐藏库用户的所有复杂性和危险点,无论他们通过调用还是继承使用该库。因此,使用 XOM,我可以安全地覆盖库类来做我想做的事情,但库保证它仍然会生成格式良好的 XML,而无需我担心可能出错的所有事情。

这种论点比通常的论点更有说服力,通常的论点带有库编写者很聪明而用户很愚蠢的潜台词。这不是关于能力,而是关于详细的知识。我希望尽可能地对 XML 的肮脏细节一无所知。通过让我不必了解这些细节,我可以将我的脑力用于我想完成的实际任务。

尽管这个有说服力的论点,但我的直觉,以及我具有赋能态度的事实,倾向于更喜欢开放继承。也许问题的关键是我们用来指示继承安全区域的机制。通常我们只有能力密封类和方法。为了安抚双方,另一种选择是能够克服密封。这样一来,你必须主动地覆盖未设计好的东西。如果你没有明确地打开密封,那么编译器只允许正常的继承,但如果你使用密封打开机制,那么编译器会将信任交给你 - 并且你将对后果负责。因此,我更愿意用“劝阻”来代替 Josh Bloch 的“禁止”。

从更广阔的视角来看,我的猜测是,我们在思考接口方面(无论是调用还是继承)还有很大的改进空间。像消费者驱动契约这样的想法需要帮助我们重新思考接口定义的含义。我不知道答案是什么,但我认为一个好的答案会让我们所有人感到惊讶。