1

今晚我在 Ward 的 wiki 上看到了 Liskov Substitution Principle 的原始声明:

这里需要的是类似于下面的替换属性:如果对于每个 S 类型的对象 o1 都有一个 T 类型的对象 o2 使得对于所有根据 T 定义的程序 P,当 o1 被替换时 P 的行为不变对于 o2,则 S 是 T 的子类型。” - Barbara Liskov,Data Abstraction and Hierarchy,SIGPLAN Notices,23,5(1988 年 5 月)。

我一直不擅长解析谓词逻辑(虽然我第一次失败了 Calc IV),所以虽然我有点理解上述内容如何转化为:

使用指向基类的指针或引用的函数必须能够在不知情的情况下使用派生类的对象。

我不明白的是为什么 Liskov 描述的属性暗示 S 是 T 的子类型,而不是相反。

也许我对 OOP 的了解还不够,但是为什么 Liskov 的陈述只允许 S -> T 的可能性,而不是 T -> S 的可能性?

4

3 回答 3

0

正如 sepp2k 所指出的,在另一篇文章中有多种观点对此进行了解释。这是我的两分钱。我喜欢这样看

如果对于每个 TallPerson 类型的对象 o1 都有一个 Person 类型的对象 o2 ,那么对于根据 Person 定义的所有程序 P,当 o1 替换 o2 时 P 的行为不变,则 TallPerson 是 Person 的子类型。(将 S 替换为 TallPerson 并将 T 替换为 Person)

我们通常认为派生一些基类的对象具有更多功能,因为它是扩展的。然而,随着更多功能的出现,我们正在对其进行专门化并缩小它们的使用范围,从而成为其基类(更广泛的类型)的子类型。

于 2010-09-05T16:44:05.073 回答
0

派生类继承其基类的公共接口,并期望使用继承的实现或提供行为相似的实现(例如,Count()无论这些元素如何存储,方法都应返回元素的数量。)

基类不一定具有其派生类的任何(更不用说all)的接口,因此期望对基类的任意引用可替代指定的派生类是没有意义的。即使看起来只需要基类接口支持的接口子集,也可能不是这种情况(例如,可能引用了特定派生类中的影子方法)。

于 2010-09-05T18:02:05.433 回答
0

假设的程序集 P(根据 T)不是根据 S 定义的,因此它对 S 没有太多说明。另一方面,我们确实说 S 在该集合中与 T 一样有效程序 P,因此我们可以得出关于 S 及其与 T 的关系的结论。

考虑这一点的一种方法是 P 要求 T 的某些属性。S 恰好满足这些属性。也许你甚至可以说“S 中的每个 o1 也在 T 中”。该结论用于定义词子类型。

于 2010-09-05T07:05:51.273 回答