如何在继承层次结构中验证Liskov替换原理?


14

灵感来自这个答案:

里氏替换原则要求

  • 前提条件不能在子类型中得到加强。
  • 子条件不能弱化后置条件。
  • 超类型的不变量必须保留在子类型中。
  • 历史记录约束(“历史记录规则”)。只能通过对象的方法(封装)将其视为可修改的对象。由于子类型可能会引入父类型中不存在的方法,因此这些方法的引入可能会导致子类型中状态不允许在父类型中发生变化。历史记录约束禁止这样做。

我希望有人能发布违反这4点的类层次结构,以及如何相应地解决它们。
我正在寻找出于教育目的的详细说明,以了解如何识别层次结构中的四个点以及解决该问题的最佳方法。

注意:
我希望发布代码示例供人们使用,但是问题本身是关于如何识别错误的层次结构的:)


Answers:


17

它比引用使声音听起来更准确,更准确得多。

当您查看继承层次结构时,请想象一个接收基类对象的方法。现在问问自己,是否存在任何假设,有人可能会编辑该方法,因此对于该类无效。

例如(最初在Bob叔叔的网站上看到):

public class Square : Rectangle
{
    public Square(double width) : base(width, width)
    {
    }

    public override double Width
    {
        set
        {
            base.Width = value;
            base.Height = value;
        }
        get
        {
            return base.Width;
        }
    }

    public override double Height
    {
        set
        {
            base.Width = value;
            base.Height = value;
        }
        get
        {
            return base.Height;
        }
    }
}

看起来很公平,对吧?我创建了一种称为Square的特殊矩形,该矩形始终保持Width必须等于Height。正方形是矩形,因此符合OO原理,不是吗?

但是,等等,如果有人现在写这个方法呢?

public void Enlarge(Rectangle rect, double factor)
{
    rect.Width *= factor;
    rect.Height *= factor;
}

不酷 但是,没有理由说这种方法的作者应该知道可能存在潜在的问题。

每次您从另一个类派生一个类时,请考虑一下基类以及人们对此可能会做些什么(例如“它具有Width和Height,并且它们都是独立的”)。然后思考“这些假设在我的子类中是否仍然有效?” 如果没有,请重新考虑您的设计。


非常好的和微妙的例子。+1。您可以做的是,使Rectangle类的方法变大,并在Square类中对其进行覆盖。
marco-fiset 2012年

@ marco-fiset:我宁愿看到Square和Rectangle解耦,Square只有一个尺寸,但是每个都实现IResizable。的确,如果有一个Draw方法,它们将是相似的,但是我希望它们都封装一个RectangleDrawer类,该类包括通用代码。
pdr 2012年

1
我认为这不是一个很好的例子。问题是正方形没有宽度或高度。它只有一面长。如果宽度和高度只能读取,则不会出现问题,但是在这种情况下它们是可写的。当引入可修改状态时,维护LSP总是要困难得多。
SpaceTrucker 2012年

@pdr感谢您的示例,但关于我在帖子中提到的4个条件,Square该类的哪一部分违反了这些条件?
Songo 2012年

1
@Songo:这是历史约束。此处提供更好的解释:blackwasp.co.uk/LSP.aspx “从本质上讲,子类包括其超类的所有方法和属性。它们也可能添加更多成员。历史约束表明,新成员修改后的成员不应修改状态以基类不允许的方式出现。例如,如果基类表示具有固定大小的对象,则子类不应允许修改此大小。”
pdr 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.