【问题标题】:Does this solve the Liskov Substitution square-rectangle violation?这是否解决了 Liskov Substitution 方形矩形违规问题?
【发布时间】:2013-08-09 08:10:48
【问题描述】:

我对 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。

【问题讨论】:

  • 这个问题是Rectangle 的客户不会期望设置一个维度会影响另一个维度。
  • 通常的解决方案是从称为 Shape 或 Polygon 等的共同祖先派生 Square 和 Rectangle。

标签: oop solid-principles liskov-substitution-principle


【解决方案1】:

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

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

LSP 声明客户端应该能够使用派生类 (Square),无论您使用其超类 (Rectangle) 的任何位置,都不会破坏 Rectangle 的业务逻辑 — 即用户应该能够将一个子类替换为另一个 &他们的其余代码不应中断。

但以下内容互不兼容:

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

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


Square/Rectangle 示例的问题在于,我们一开始就对 Rectangle 做了太多假设。矩形 可以 具有与高度不同的长度,但这是特定类型矩形(长方形)的属性。

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

【讨论】:

    【解决方案2】:

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

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

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

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

    【讨论】:

    • 我会说具有可独立设置的宽度和高度并不是矩形的固有属性。一个矩形仍然是一个矩形,即使它的宽度和高度是不可变的。因此,对于任何给定的矩形,人们不能假设任何关于其尺寸的约束(或缺乏约束)是合理的。我想说一个保证具有独立可调宽度和高度的矩形,实际上不是一个纯矩形,它是一个矩形加上某些额外的保证行为。
    • “替换子类的对象不应该改变行为”并不是真的。Liskov 原则说如果程序模块正在使用基类,那么对基类的引用可以替换为一个不影响功能的派生类
    猜你喜欢
    • 2015-01-01
    • 2020-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-12
    相关资源
    最近更新 更多