当我注意到这个答案时,我一直在阅读里氏替换原理。它有一个
Circle
和一个 ColoredCircle
类型,其中 ColoredCircle
的构造函数需要一个额外的参数; color
。
class Circle:
radius: int
def __init__(self, radius: int) -> None:
self.radius = radius
class ColoredCircle(Circle):
radius: int
color: str
def __init__(self, radius: int, color: str) -> None:
super().__init__(radius)
self.color = color
这是否违反了以下要求之一? (摘自这个答案)。在
ColoredCircle
的情况下,唯一的其他选项是公共变量或 set_color
方法。
无法强化先决条件:假设你的基类有效 与成员 int。现在你的子类型要求 int 为正数。 这是强化的先决条件,现在任何有效的代码 在负整数被破坏之前完全没问题。
如果我在这里搜索的方向错误,请告诉我。另外,如果一个子类型有更多的参数需要处理,通常如何管理这些参数,新的抽象总是必要的吗?
当类
X
有构造函数时,该构造函数不是类型为X
的对象的方法。由于它不是 X
类型对象上的方法,因此它也不必作为派生类型对象上的方法存在——它与 LSP 无关。
里氏替换原则的目的是类型及其子类型应该是可替换的,这反过来又允许解耦。消费者不必知道对象的实现,只需知道其声明的类型。如果一个类通过调用其构造函数来创建自己的依赖项,则它会耦合到该特定类型。在这种情况下,LSP 就变得无关紧要了。无法替换其他类型,因此类型是否可替换并不重要。
换句话说,创建另一个类的实例的类通常无法从 LSP 中受益,因为它主要排除了用一种类型替换另一种类型的可能性。如果它将该对象传递给其他方法,这些方法以创建对象之外的方式与其交互,那么我们就可以从 LSP 中受益。
基于这个推理,我想说不同的构造函数并不违反里氏替换原则的意图。
在许多语言中,我们使用依赖项注入将类与依赖项的构造分离。这意味着消费者处理类型的各个方面,除了它的构造函数。构造函数不在等式中,子类型可以(或应该)替换它们继承的类型。
LSP 谈论并专注于替换
实例,而不是“类”本身!
有代码:class Circle:
def __init__(self, radius: int) -> None:
self.radius = radius
class ColoredCircle(Circle):
def __init__(self, radius: int, color: str) -> None:
super().__init__(radius)
self.color = color
circle_instance = Circle(10)
colored_circle_instance = ColoredCircle(20, "blue")
def print_radius(obj: Circle) -> None:
print(f"The radius is: {obj.radius}")
无论您需要
Circle
类型的实例(即circle_instance
),都可以传递
ColoredCircle
类型的实例(即
colored_circle_instance
)。 LSP 在这里不会受到侵犯。您可以在
circle_instance
上、在
colored_circle_instance
上做任何可能的事情。如果您有实例,则意味着初始化程序之前必须已调用过。
什么时候会出问题?如果您有不兼容的方法,可以在实例上调用:
class Circle:
def __init__(self, radius: int) -> None:
self.radius = radius
def method(self, arg) -> None:
print(f"calling method with arg: {arg}")
class ColoredCircle(Circle):
def __init__(self, radius: int, color: str) -> None:
super().__init__(radius)
self.color = color
def method(self, arg, arg2) -> None:
print(f"calling method with arg: {arg, arg2}")
甚至 Pylance 也会告诉你:
Method "method" overrides class "Circle" in an incompatible manner
Positional parameter count mismatch; base method has 2, but override has 3PylancereportIncompatibleMethodOverride
有人可能会说类本身就是实例。是的,但那是另一个故事了。如果我们想讨论在传递实际“类”(
Circle
和 ColoredCircle
)时是否违反 LSP(例如用于实例化),则必须存在元类的层次结构。