4

面向对象编程的推荐原则之一是Liskov 替换原则:子类的行为方式应与其基类相同(警告:这实际上不是对 Liskov 原则的正确描述:参见 PS) .

是否建议它也适用于构造函数?我主要考虑 Python 及其__init__()方法,但这个问题适用于任何具有继承的面向对象语言。

我问这个问题是因为有时从一个或多个提供一些不错的默认行为的类继承子类很有用(比如在 Python 中从字典继承,因此obj['key']适用于新类的对象)。然而,允许子类完全像字典一样使用并不总是自然或简单的:构造函数参数只与特定的用户子类相关(例如,表示一组串行端口的类)有时会更好可能想表现得像一本字典ports['usb1']是 USB 端口 #1 等)。对于这种情况,推荐的方法是什么?拥有与其基类完全兼容的子类构造函数,并通过采用简单、用户友好参数的对象工厂函数生成实例?还是简单地编写一个类构造函数,其参数集不能直接提供给其基类的构造函数,但从用户的角度来看哪个更合乎逻辑?

PS:我误解了上面的 Liskov 原则:Sven 在下面的评论指出子类的对象应该表现得像超类的对象(子类本身不必表现得像超类;特别是它们的构造函数没有具有相同的参数[签名])。

4

1 回答 1

4

根据要求,我发布了以前的评论作为答案。

链接的 Wikipedia 文章中定义的原则是“如果 S 是 T 的子类型,则 T 类型的对象可以被 S 类型的对象替换”。它没有读作“子类的行为方式应与其基类相同”。在考虑构造函数时,区别很重要:维基百科版本只讨论子类型的对象,而不是类型本身。对于一个对象,构造函数已经被调用,所以这个原理不适用于构造函数。这也是我应用它的方式,以及它在标准库中的应用方式(例如defaultdictdict)。

多重继承中的构造函数可能无法以与语言无关的方式进行讨论。在 Python 中,有两种方法。如果您的继承图包含菱形模式,并且您需要确保所有构造函数都只调用一次,则应使用super()并遵循 Raymond Hettinger 的文章Python 的super()被认为是 super的“实用建议”部分中描述的模式。如果您没有菱形(包括 的菱形除外object),您还可以对所有基类构造函数使用显式基类调用。

于 2012-01-24T01:02:08.697 回答