Does this solve the Liskov Substitution square-rectangle violation?

邮差的信 提交于 2019-12-01 23:26:21

问题


I'm very new to the SOLID design principles. One thing I had problem with understanding is the "Square-rectangle" example of a Liskov Substition Principle violation. Why should the Height/Width setter of a Square override the ones of a Rectangle? Isn't this exactly what's causing the problem when there's Polymorphism?

Doesn't removing this solve the problem?

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();
    }
}

Output is 30 as expected.


回答1:


Imagine the user is implementing a bounding box in a GUI application, similar to this:

They want to represent this blue box by a Rectangle class, so that if the user clicks & drags down its height will increase; if the user drags right, its width will increase.

LSP states that a client should be able to use a derived class (Square) wherever you would use its superclass (Rectangle) without breaking the business logic of Rectangle — i.e. a user should be able to sub in one for the other & the rest of their code shouldn't break.

But the following are incompatible with each other:

  • It's an assumed post-condition of Rectangle that it's setter methods won't cause side effects (i.e. setWidth shouldn't affect the height)
  • It's inherent to the logic of a Square that its width will always equal its height.

If the programmer used Square instead of Rectangle, their assumption above wouldn't work, as if the user dragged down, the box would get bigger horizontally & vertically at the same time.


The trouble with the Square/Rectangle example is that we're assuming too much about Rectangle to begin with. A rectangle can have a different length to its height, but this is a property of a specific type of rectangle (an oblong rectangle).

A square is a rectangle, but a square is not an oblong rectangle. If we want to assume the behaviour of an oblong about our Rectangle class (that it's width & height can differ), it's then doesn't make sense for our Square class to extend from that.




回答2:


The LSP states that substituting an object of a subclass should not change the behaviour, or the correctness, of the program. The classes you specify do change the correctness. With a rectangle, the client of the class expects that the height and width are independently settable. When you subclass with Square, this is no longer the case.

A client setting a width of 5 and a height of 10, whilst reference an object that happens to be a Square but is held in a Rectangle variable, will get different results according to the order in which they set the height and width properties. They might get a 5x5 rectangle or a 10x10 one. Either case will be unexpected.

There's Barbara's original complex description of LSP but Uncle Bob's makes it easier - "Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it". This is broken with the Square/Rectangle problem.

I wrote an article about this at http://www.blackwasp.co.uk/SquareRectangle.aspx.



来源:https://stackoverflow.com/questions/18142543/does-this-solve-the-liskov-substitution-square-rectangle-violation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!