Liskov替代原则声明:
超类型的不变量必须保留在子类型中。
我对这个原则和多态性的交集特别感兴趣。事实上,特别是亚型多态性,参数多态和Haskell类型似乎就是这种情况。
所以,我知道当函数的参数是逆变的并且它们的返回类型是协变的时,函数是子类型。我们可以假设方法只是具有隐式" self"论点。但是,这似乎暗示如果子类重写父类的方法,则它不再是子类型,因为它的一个方法不再是子类型。
例如。请使用以下伪代码:
class Parent:
count : int
increment : Parent -> ()
{
count += 1
}
class Child inherits Parent:
increment : Child -> ()
{
count += 2
}
回到LSP:我们可以说Parent.increment()
的属性应该适用于Child.increment()
,即使这两个属性不遵守严格的子类型关系吗?
更一般地说,我的问题是:子类型的规则如何与多态函数的更具体的参数接口,以及正确思考这两个概念的正确方法是什么?
答案 0 :(得分:0)
引用维基百科关于Liskov Substitution Principle
的文章更正式地说,Liskov替代原则(LSP)是一个特殊的 定义子类型关系,称为(强)行为 子类型 [...]
行为子类型比典型的亚型更强的概念 类型理论中定义的函数,它仅依赖于 参数类型的逆变和返回类型的协方差。 一般来说,行为子类型通常是不可判定的[...]
子类型必须具有许多行为条件 满足:
- 无法在子类型中加强先决条件。
- 后置条件不能在子类型中被削弱。
- 超类型的不变量必须保留在子类型中。
因此,LSP是一种更强的子类型定义,它依赖于类型理论之外的特征。
在你的例子中,这会上升并落在你的不变量上。
calling increment will increase count by **exactly 1**
显然 Child 无法用 Parent 表示,因为不变量已被破坏。这不能仅从语法中推断出来。
LSP应该引导您单独定义父级和子级,让它们都从Incrementable
继承,后者条件较弱。
答案 1 :(得分:0)
术语“子类型”在技术上是一个句法问题。所以语法上,Child <: Parent
。
Liskov原则是关于行为子类型,如维基百科所述。它需要语法子类型,但它还取决于你对类的不变性和前/后条件的定义。由于您没有定义任何内容,因此谈论违规行为是毫无意义的。
如果您将increment
的后置条件定义为new count = old count + 1
,则存在违规行为。
如果您将increment
的后置条件定义为new count > old count
,则没有。{/ p>
通常,将后置条件定义为“完全是父级的后置条件”会使包含多态性无法通过定义。在多态有意义的地方,应该放宽后置条件的定义。
请注意,类不变量是关于可能的值 - 对象的快照 - 并且因为您可以根据Parent increment
定义Child increment
,所以它不能违反任何不变量。