3

我对 SOLID 设计原则非常陌生。我无法理解的一件事是 Liskov Substition Principle 违反的“方形矩形”示例。为什么 Square 的 Height/Width 设置器应该覆盖 Rectangle 的设置器?当存在多态性时,这不正是导致问题的原因吗?

删除它不能解决问题吗?

class Rectangle
{
    public /*virtual*/ double Height { get; set; }
    public /*virtual*/ double Width { get; set; }
    public double Area() { return Height * Width; }
}

class Square : Rectangle
{
    double _width; 
    double _height;
    public /*override*/ double Height
    {
        get
        {
            return _height;
        }
        set
        {
            _height = _width = value;
        }
    }
    public /*override*/ double Width
    {
        get
        {
            return _width;
        }
        set
        {
            _width = _height = value;
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        Rectangle r = new Square();
        r.Height = 5;
        r.Width = 6;

        Console.WriteLine(r.Area());
        Console.ReadLine();
    }
}

正如预期的那样,输出为 30。

4

2 回答 2

10

想象一下,用户正在 GUI 应用程序中实现一个边界框,类似于:

在此处输入图像描述

他们想用一个Rectangle类来表示这个蓝色框,这样如果用户点击并向下拖动它的高度就会增加;如果用户向右拖动,它的宽度会增加。

LSP 声明客户端应该能够在任何您使用其超类(矩形)的地方使用派生类(方形),而不会破坏矩形的业务逻辑——即,用户应该能够将一个子类插入另一个类和其余部分他们的代码不应该破坏。

但以下是互不兼容的:

  • 这是 Rectangle 的假设后置条件,它的 setter 方法不会引起副作用(即setWidth不应该影响高度)
  • Square 的逻辑固有的是它的宽度总是等于它的高度。

如果程序员使用 Square 而不是 Rectangle,他们上面的假设就行不通了,就像用户向下拖动一样,盒子会同时在水平和垂直方向上变大。


Square/Rectangle 示例的问题在于,我们对 Rectangle 的假设过多。矩形的长度与高度可以不同,但​​这是特定类型矩形(长方形)的属性。

正方形长方形,但正方形不是长方形。如果我们想假设我们的类有一个长方形的行为(它的宽度和高度可以不同),那么我们的类从它扩展Rectangle就没有意义了。Square

于 2013-08-15T21:45:00.287 回答
1

LSP 声明替换子类的对象不应改变程序的行为或正确性。您指定的类确实会改变正确性。对于矩形,类的客户端期望高度和宽度是可独立设置的。当您使用 Square 进行子类化时,情况不再如此。

客户端设置宽度为 5,高度为 10,同时引用恰好是 Square 但保存在 Rectangle 变量中的对象,将根据设置高度和宽度属性的顺序获得不同的结果。他们可能会得到一个 5x5 或 10x10 的矩形。这两种情况都会出乎意料。

有 Barbara 对 LSP 的原始复杂描述,但 Bob 叔叔的描述更容易——“使用指针或对基类的引用的函数必须能够在不知情的情况下使用派生类的对象”。方形/矩形问题打破了这一点。

我在http://www.blackwasp.co.uk/SquareRectangle.aspx上写了一篇关于此的文章。

于 2013-08-15T22:12:38.557 回答