Square和Rectangleinheritance有什么问题?

我已经阅读了一些关于使Square成为Rectangle类的inheritance类的做法的一些文章,这说明它违反了LSP(Liskov替换原则)。 我仍然没有得到它,我在Ruby中做了一个示例代码:

class Rectangle attr_accessor :width, :height def initialize(width, height) @width = width @height = height end end class Square < Rectangle def initialize(length) super(length, length) end def width=(number) super(number) @height = number end def height=(number) super(number) @width = number end end s = Square.new(100) s.width = 50 puts s.height 

谁能告诉我它有什么问题?

我并不总是热衷于Liskov,因为它似乎限制了你可以根据行为而不是“本质”来inheritance你的行为。 在我看来,inheritance总是意味着“是一种”关系,而不是“行为完全像”一种关系。

话虽如此, 维基百科的文章详细说明了为什么一些人认为这很糟糕,使用您的确切示例:

违反LSP的典型示例是Square类,它派生自Rectangle类,假设宽度和高度都存在getter和setter方法。

Square类总是假设宽度等于高度。 如果在期望Rectangle的上下文中使用Square对象,则可能会发生意外行为,因为Square的维度不能(或者不应该)独立修改。

这个问题不容易修复:如果我们可以修改Square类中的setter方法以保持Square不变量(即保持尺寸相等),那么这些方法将削弱(违反)Rectangle setter的后置条件,声明尺寸可以独立修改。

因此,查看代码和等效的Rectangle代码:

 s = Square.new(100) r = Rectangle.new(100,100) s.width = 50 r.width = 50 puts s.height puts r.height 

输出将在左侧为50,在右侧为100。

但是,在我看来, 是文章的重点:

像这样的LSP的违反在实践中可能也可能不是问题,这取决于使用违反LSP的类的代码实际预期的后置条件或不变量。

换句话说,如果使用类的代码理解行为,则没有问题。

底线,正方形是矩形的适当子集,对于矩形的宽松定义:-)

从Liskov替换原则(LSP)的角度来看,它的错误在于你的RectangleSquare s是可变的。 这意味着您必须显式重新实现子类中的setter,并失去inheritance的好处。 如果你使Rectangle不可变,也就是说,如果你想要一个不同的Rectangle你创建一个新的而不是改变现有的Rectangle ,那么违反LSP是没有问题的。

 class Rectangle attr_reader :width, :height def initialize(width, height) @width = width @height = height end def area @width * @height end end class Square < Rectangle def initialize(length) super(length, length) end end 

使用attr_reader给出了getter而不是setter,因此具有不变性。 通过这种实现, RectanglesSquares提供了heightwidth可见性,对于正方形,它们将始终相同,并且区域的概念是一致的。

考虑抽象基类或接口(无论是接口还是抽象类都是与LSP无关的实现细节) ReadableRectangle ; 它具有只读属性WidthHeight 。 可以从类型ReadableSquare派生,它具有相同的属性,但在合同上保证WidthHeight始终相等。

ReadableRectangle ,可以定义具体类型ImmutableRectangle (在构造函数中获取高度和宽度,并保证HeightWidth属性将始终返回相同的值)和MutableRectangle 。 还可以定义具体类型MutableRectangle ,它允许随时设置高度和宽度。

在“方形”方面, ImmutableSquare应该可以替代ImmutableRectangleReadableSquare 。 但是, MutableSquare只能替代ReadableSquare [它又可替代ReadableRectangle 。]此外,虽然ImmutableSquare的行为可替代ImmutableRectangle ,但inheritance具体的ImmutableRectangle类型所获得的值将受到限制。 如果ImmutableRectangle是一个抽象类型或接口, ImmutableSquare类只需要使用一个字段而不是两个字段来保存它的维度(对于一个有两个字段的类,保存一个没什么大不了的,但是不难想象有一个类更多的领域,节省可能是重要的)。 但是,如果ImmutableRectangle是具体类型,那么任何派生类型都必须包含其基类的所有字段。

某些类型的正方形可替代相应类型的矩形,但可变正方形不可替代可变矩形。

Interesting Posts