首页 > 解决方案 > 重载(而不是压倒一切)是否违反了 Liskov 替换原则?

问题描述

有大量关于 LSP 的讨论,但所有讨论似乎都过于模糊。
AFAIK,LSP 指出要正确覆盖(而不是重载)子类中的超类方法,应确保子方法:

  1. 不会产生父方法在任何情况下都不会引发的新异常类型
  2. 与父方法具有相同的签名(在强类型语言的情况下)
  3. 与签名具有相同的语义含义
  4. 返回相同类型的值
  5. 返回具有相同语义的值

我的意思是,semantic meaning如果基类方法暗示它返回int并且这int意味着美元或欧元,那么重载的方法也应该暗示返回的值是美元或欧元,并且即使在一种情况下返回“RUB”也会违反语言服务提供商。

但是如果我的基类看起来像这样(示例在 Python 中):

class A:

    func(x: int) -> int
        return x*2

class B(A):

    func(x: int, y: string) -> int
        return x*y

所以我的问题中有两个子问题:

  1. contract这个词在更实际的意义上是什么意思?是同义词interface吗?
  2. 使用基类中不存在的签名(作为参数列表的一部分)重载是否违反 LSP?

标签: pythonoopdesign-patternsliskov-substitution-principle

解决方案


答案取决于语言。

在 Java、C++、C# 等典型的 stronly 类型的 OO 语言中,当您编写类似的方法调用时a.func(b,c),实际调用的方法取决于方法名称以及接收类型(a本例中为),以及参数的数量和类型。

在这样的语言中,具有不同数量的参数或不同类型的参数的方法是完全不同的方法。拥有不同数量的参数就像拥有不同的名称一样。当您“重载”一个方法时,就像创建一个具有不同名称的方法一样,因此您不会通过重载基类中的方法来违反 LSP。

不过,您的问题似乎与 python 有关,并且在典型的动态类型语言(如 python、JavaScript 等)中,当您编写类似a.func(b,c)的方法调用时,仅按名称查找要调用的方法(在与接收对象)。在这样的语言中,没有方法或函数的重载。

在您的示例中,您已经使用双参数函数覆盖了单参数定义。func这意味着派生类的消费者不能再func使用一个参数调用,这确实违反了 LSP。


推荐阅读