Това решава ли нарушението на Liskov Substitution square-правоъгълник?

Аз съм много нов в принципите на дизайна на SOLID. Едно нещо, което имах проблем с разбирането, е примерът "Квадрат-правоъгълник" за нарушение на принципа на заместване на Лисков. Защо сетерът за височина/ширина на квадрат трябва да отменя тези на правоъгълник? Не е ли точно това, което причинява проблема, когато има полиморфизъм?

Премахването на това не решава ли проблема?

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, както се очаква.


person Ivan Prodanov    schedule 09.08.2013    source източник
comment
Проблемът с това е, че клиентите на Rectangle няма да очакват настройката на едно измерение да повлияе на другото.   -  person Lee    schedule 09.08.2013
comment
Обичайното решение е да се извлекат както Square, така и Rectangle от общ предшественик, наречен Shape или Polygon и т.н.   -  person David Elliman    schedule 09.08.2013


Отговори (2)


Представете си, че потребителят внедрява ограничителна рамка в GUI приложение, подобно на това:

въведете описание на изображението тук

Те искат да представят това синьо поле с Rectangle клас, така че ако потребителят щракне и плъзне надолу, височината му ще се увеличи; ако потребителят плъзне надясно, ширината му ще се увеличи.

LSP заявява, че клиентът трябва да може да използва производен клас (Square), където и да използвате неговия суперклас (Rectangle), без да нарушава бизнес логиката на Rectangle - т.е. кодът им не трябва да се разваля.

Но следните са несъвместими помежду си:

  • Предполага се следусловие на Rectangle, че методите му за настройка няма да причинят странични ефекти (т.е. setWidth не трябва да влияе на височината)
  • Присъщо на логиката на квадрата е, че неговата ширина винаги ще е равна на неговата височина.

Ако програмистът използва Square вместо Rectangle, тяхното предположение по-горе няма да работи, тъй като ако потребителят дърпа надолу, кутията ще стане по-голяма хоризонтално и вертикално едновременно.


Проблемът с примера с квадрат/правоъгълник е, че ние предполагаме твърде много за правоъгълника като начало. Един правоъгълник може да има различна дължина от височината си, но това е свойство на специфичен тип правоъгълник (продълговат правоъгълник).

Квадратнае правоъгълник, но квадратнае продълговат правоъгълник. Ако искаме да приемем поведението на продълговат за нашия Rectangle клас (че ширината и височината му могат да се различават), тогава няма смисъл нашият Square клас да се разширява от това.

person anotherdave    schedule 15.08.2013

LSP заявява, че заместването на обект от подклас не трябва да променя поведението или коректността на програмата. Посочените от вас класове променят коректността. С правоъгълник клиентът на класа очаква, че височината и ширината могат да се настройват независимо. Когато подкласирате с Square, това вече не е така.

Клиент, задаващ ширина от 5 и височина от 10, докато препраща към обект, който случайно е Square, но се държи в променлива Rectangle, ще получи различни резултати според реда, в който задава свойствата за височина и ширина. Може да получат правоъгълник 5x5 или 10x10. И двата случая ще бъдат неочаквани.

Има оригиналното сложно описание на LSP на Барбара, но това на чичо Боб го прави по-лесно - "Функциите, които използват указатели или препратки към базови класове, трябва да могат да използват обекти от производни класове, без да го знаят". Това е нарушено с проблема с квадрата/правоъгълника.

Написах статия за това на http://www.blackwasp.co.uk/SquareRectangle.aspx.

person BlackWasp    schedule 15.08.2013
comment
Бих казал, че ширината и височината, които могат да се задават независимо, не е присъщо свойство на правоъгълник. Правоъгълникът все още е правоъгълник, дори ако ширината и височината му са неизменни. Следователно за всеки даден правоъгълник е разумно човек да не допуска нищо относно ограничения (или липса на ограничения) върху неговите размери. Бих казал, че правоъгълник, за който е гарантирано, че има независимо регулируема ширина и височина, всъщност не е чист правоъгълник, това е правоъгълник ПЛЮС определено допълнително гарантирано поведение. - person Brad Thomas; 24.12.2016
comment
заместването на обект от подклас не трябва да променя поведението всъщност не е вярно. Принципът на Liskov казва, че ако програмен модул използва базов клас, тогава препратката към базовия клас може да бъде заменена с производен клас, без да се засяга функционалността - person jpl; 24.11.2017