需要清楚了解 liskov 替换原则
Need clarity in Understanding liskov substitution principle
LSP 定义指出,如果 S 是 T 的子类型,则程序中类型 T 的对象可以替换为类型 S 的对象,而不会改变该程序的任何所需属性。
- 前置条件不能在子类型中加强
- 不能在子类型中弱化后置条件。
- 超类型的不变量必须保留在子类型中。
比如我下面有类
这是否违反了(前提条件不能在子类型中得到加强)。
我正在努力解决这个问题,有人可以提供一个很好的例子来理解它吗?
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 定义了一个仅关于其属性的接口,也就是说,它具有 ip
和 dc
字段。由于您在 CiscoSwitch
和 JuniperSwitch
中调用 super
,它们都有这些字段,因此实现 Switch
的 "interface" 并且它们可能会替换它。
现在,让我们来解决您提到的情况:
- 无法在子类型中加强前置条件
假设您有一个接口,其中包含一个为形状着色的方法,并且此方法仅接受十六进制颜色 ("#123456"
) 或 rgb 颜色 ((123,124,125)
)。假设您创建了一个实现此接口的 class 矩形,但它只接受 rgb 颜色。你在强化先决条件。如果您的代码中有这一行:
generic_shape.color("#123456")
你不能在其中替换你的矩形,所以你破坏了 LSP。
- 后置条件不能在子类型中弱化
一个 class 典型的例子是 returning None
一个不应该 return None
的方法。假设在Shape接口中,你有一个getSide
方法,你创建了一个Circle
class,在实现这个方法的时候,你returnNone
。这打破了调用者的预期并可能导致意外错误:
side = generic_shape.get_side() # suppose it is a circle
scaled_side = side * 2 # you get an error
- 超类型的不变量必须保留在子类型中
这个在我看来比较复杂,因为很难说清楚。我能想到的唯一例子是保持 属性 非空,假设在你的例子中你有一个不使用 IP 的交换机(可能使用 MAC-addr),如果交换机接口期望ip
字段不应该是 None,你的 MAC-Switch 会破坏这个接口。
希望对您有所帮助!
LSP 定义指出,如果 S 是 T 的子类型,则程序中类型 T 的对象可以替换为类型 S 的对象,而不会改变该程序的任何所需属性。
- 前置条件不能在子类型中加强
- 不能在子类型中弱化后置条件。
- 超类型的不变量必须保留在子类型中。
比如我下面有类 这是否违反了(前提条件不能在子类型中得到加强)。 我正在努力解决这个问题,有人可以提供一个很好的例子来理解它吗?
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 定义了一个仅关于其属性的接口,也就是说,它具有 ip
和 dc
字段。由于您在 CiscoSwitch
和 JuniperSwitch
中调用 super
,它们都有这些字段,因此实现 Switch
的 "interface" 并且它们可能会替换它。
现在,让我们来解决您提到的情况:
- 无法在子类型中加强前置条件
假设您有一个接口,其中包含一个为形状着色的方法,并且此方法仅接受十六进制颜色 ("#123456"
) 或 rgb 颜色 ((123,124,125)
)。假设您创建了一个实现此接口的 class 矩形,但它只接受 rgb 颜色。你在强化先决条件。如果您的代码中有这一行:
generic_shape.color("#123456")
你不能在其中替换你的矩形,所以你破坏了 LSP。
- 后置条件不能在子类型中弱化
一个 class 典型的例子是 returning None
一个不应该 return None
的方法。假设在Shape接口中,你有一个getSide
方法,你创建了一个Circle
class,在实现这个方法的时候,你returnNone
。这打破了调用者的预期并可能导致意外错误:
side = generic_shape.get_side() # suppose it is a circle
scaled_side = side * 2 # you get an error
- 超类型的不变量必须保留在子类型中
这个在我看来比较复杂,因为很难说清楚。我能想到的唯一例子是保持 属性 非空,假设在你的例子中你有一个不使用 IP 的交换机(可能使用 MAC-addr),如果交换机接口期望ip
字段不应该是 None,你的 MAC-Switch 会破坏这个接口。
希望对您有所帮助!