需要清楚了解 liskov 替换原则

Need clarity in Understanding liskov substitution principle

LSP 定义指出,如果 S 是 T 的子类型,则程序中类型 T 的对象可以替换为类型 S 的对象,而不会改变该程序的任何所需属性。

  1. 前置条件不能在子类型中加强
  2. 不能在子类型中弱化后置条件。
  3. 超类型的不变量必须保留在子类型中。

比如我下面有类 这是否违反了(前提条件不能在子类型中得到加强)。 我正在努力解决这个问题,有人可以提供一个很好的例子来理解它吗?

Class Switch:
   def __init__(self,ip,dc):
       self.ip=ip
       self.dc=dc

class CiscoSwitch(Switch):
   def __init__(self,ip,dc,zone):
       super().__init__(ip,dc)
       self.zone=zone

class JuniperSwitch(Switch):
   def __init__(self,ip,dc,zone):
       super().__init__(ip,dc)
       self.zone=zone

LSP的原理是通过接口实现的,也就是object/value中的public。在 Python 的情况下,您具有 public 的属性和方法,因此,如果您要实现一个接口,则必须遵守它。 在您的示例中, Switch class 定义了一个仅关于其属性的接口,也就是说,它具有 ipdc 字段。由于您在 CiscoSwitchJuniperSwitch 中调用 super,它们都有这些字段,因此实现 Switch 的 "interface" 并且它们可能会替换它。

现在,让我们来解决您提到的情况:

  1. 无法在子类型中加强前置条件

假设您有一个接口,其中包含一个为形状着色的方法,并且此方法仅接受十六进制颜色 ("#123456") 或 rgb 颜色 ((123,124,125))。假设您创建了一个实现此接口的 class 矩形,但它只接受 rgb 颜色。你在强化先决条件。如果您的代码中有这一行: generic_shape.color("#123456")

你不能在其中替换你的矩形,所以你破坏了 LSP。

  1. 后置条件不能在子类型中弱化

一个 class 典型的例子是 returning None 一个不应该 return None 的方法。假设在Shape接口中,你有一个getSide方法,你创建了一个Circleclass,在实现这个方法的时候,你returnNone。这打破了调用者的预期并可能导致意外错误:

side = generic_shape.get_side() # suppose it is a circle
scaled_side = side * 2 # you get an error
  1. 超类型的不变量必须保留在子类型中

这个在我看来比较复杂,因为很难说清楚。我能想到的唯一例子是保持 属性 非空,假设在你的例子中你有一个不使用 IP 的交换机(可能使用 MAC-addr),如果交换机接口期望ip 字段不应该是 None,你的 MAC-Switch 会破坏这个接口。

希望对您有所帮助!