Подтвердить что ты не робот

Выводит ли квадрат из прямоугольника нарушением Принципа замещения Лискова?

Я новичок в разработке и изучении принципов дизайна.

Говорят, что выведение квадрата из прямоугольника является классическим примером нарушения принципа замещения Лискова.

Если это так, каков должен быть правильный дизайн?

4b9b3361

Ответ 1

Я считаю, что аргументация такова:

Скажем, у вас есть метод, который принимает прямоугольник и регулирует его ширину:

public void SetWidth(Rectangle rect, int width)
{
    rect.Width = width;
}

Должно быть совершенно разумно, учитывая, что такое прямоугольник, предположить, что этот тест пройдет:

Rectangle rect = new Rectangle(50, 20); // width, height

SetWidth(rect, 100);

Assert.AreEqual(20, rect.Height);

... потому что изменение ширины прямоугольника не влияет на его высоту.

Однако скажем, вы получили новый квадратный класс из Rectangle. По определению квадрат имеет высоту и ширину, всегда равные. Повторите попытку:

Rectangle rect = new Square(20); // both width and height

SetWidth(rect, 100);

Assert.AreEqual(20, rect.Height);

Этот тест завершится неудачно, поскольку установка квадратной ширины до 100 также изменит ее высоту.

Таким образом, принцип подстановки Лискова нарушается путем выведения квадрата из прямоугольника.

Правило "is-a" имеет смысл в "реальном мире" (квадрат определенно является своего рода прямоугольником), но не всегда в мире разработки программного обеспечения.

Edit

Чтобы ответить на ваш вопрос, правильный дизайн, вероятно, должен состоять в том, что Rectangle and Square получают из общего класса "Polygon" или "Shape", который не применяет никаких правил относительно ширины или высоты.

Ответ 2

Ответ зависит от изменчивости. Если ваши прямоугольники и квадратные классы неизменяемы, то Square действительно является подтипом Rectangle, и он отлично подходит для получения первого из второго. В противном случае Rectangle и Square могли бы выставлять IRectangle без мутаторов, но вывод одного из другого неверен, так как ни один из них не является соответствующим подтипом другого.

Ответ 3

Я не согласен с тем, что вывод квадрата из прямоугольника обязательно нарушает LSP.

В примере с Matt, если у вас есть код, который зависит от ширины и высоты независимо, то он фактически нарушает LSP.

Если, однако, вы можете заменить прямоугольник на квадрат везде в вашем коде, не нарушая никаких предположений, тогда вы не нарушаете LSP.

Итак, это действительно сводится к тому, что прямоугольник абстракции означает в вашем решении.

Ответ 4

В последнее время я много борюсь с этой проблемой и думал, что добавлю шляпу в кольцо:

public class Rectangle {

    protected int height;    
    protected int width;

    public Rectangle (int height, int width) {
        this.height = height;
        this.width = width;
    }

    public int computeArea () { return this.height * this.width; }
    public int getHeight () { return this.height; }
    public int getWidth () { return this.width; }

}

public class Square extends Rectangle {

    public Square (int sideLength) {
        super(sideLength, sideLength);
    }

}

public class ResizableRectangle extends Rectangle {

    public ResizableRectangle (int height, int width) {
        super(height, width);
    }

    public void setHeight (int height) { this.height = height; }
    public void setWidth (int width) { this.width = width; }

}

Обратите внимание на последний класс, ResizableRectangle. Перемещая "resizableness" в подкласс, мы получаем повторное использование кода, фактически улучшая нашу модель. Подумайте об этом так: квадрат не может быть свободно изменен, оставаясь квадратом, в то время как прямоугольники не квадратные. Не все прямоугольники могут быть изменены, поскольку квадрат является прямоугольником (и он не может быть свободно изменен, сохраняя при этом его "идентичность" ). (o_O) Таким образом, имеет смысл создать базовый класс Rectangle, который не может быть изменен, поскольку это дополнительное свойство некоторых прямоугольников.

Ответ 5

Предположим, что у нас есть класс Rectangle с двумя (для простоты public) свойствами width, height. Мы можем изменить эти два свойства: r.width = 1, r.height = 2.
Теперь мы говорим, что квадрат is_a Rectangle. Но хотя претензия "квадрат будет вести себя как прямоугольник", мы не можем установить .width = 1 и .height = 2 на квадратный объект (ваш класс, вероятно, будет регулировать ширину, если вы установите высоту и наоборот). Итак, существует хотя бы один случай, когда объект типа Square не ведет себя как прямоугольник, и поэтому вы не можете его заменить (полностью).

Ответ 6

Я считаю, что существуют методы OOD/OOP, позволяющие программному обеспечению представлять реальный мир. В реальном мире квадрат представляет собой прямоугольник с равными сторонами. Квадрат - это квадрат только потому, что он имеет равные стороны, а не потому, что он решил быть квадратом. Поэтому программа OO должна справиться с этим. Конечно, если подпрограмма, создающая объект, хочет, чтобы он был квадратным, он мог бы указать свойство length и свойство width равным одной и той же сумме. Если программа, использующая объект, должна знать позже, если она квадратная, ее нужно только спросить. Объект может иметь логическое свойство, доступное только для чтения, которое называется "Квадрат". Когда вызывающая процедура вызывает его, объект может вернуться (Length = Width). Теперь это может иметь место, даже если объект прямоугольника является неизменным. Кроме того, если прямоугольник действительно неизменен, значение свойства Square можно задать в конструкторе и выполнить с ним. Почему же это проблема? LSP требует, чтобы под-объекты были неизменными для применения, а квадрат - под-объект прямоугольника часто используется в качестве примера его нарушения. Но это не похоже на хороший дизайн, потому что, когда используемая процедура вызывает объект как "objSquare", должна знать его внутреннюю детализацию. Не было бы лучше, если бы не волнует, был ли прямоугольник квадратным или нет? И это было бы потому, что методы прямоугольников были бы правильными независимо. Есть ли лучший пример того, когда LSP нарушается?

Еще один вопрос: как объект неизменен? Есть ли свойство "Неизменяемое", которое может быть установлено при создании экземпляра?

Я нашел ответ, и это то, что я ожидал. Поскольку я разработчик VB.NET, это то, что меня интересует. Но понятия одинаковы на разных языках. В VB.NET вы создаете неизменяемые классы, создавая свойства для чтения только для чтения, и вы используете конструктор New, чтобы позволить экземпляру-подпрограмме задавать значения свойств при создании объекта. Вы также можете использовать константы для некоторых свойств, и они всегда будут одинаковыми. От создания вперед объект неизменен.

Ответ 7

Проблема заключается в том, что описываемое действительно не является "типом", а является совокупным появляющимся свойством.

Все, что у вас есть, это четырехугольник и что "прямоугольность" и "прямоугольность" - это просто возникающие артефакты, полученные из свойств углов и сторон.

Вся концепция "Квадрат" (или даже прямоугольник) представляет собой просто абстрактное представление совокупности свойств объекта по отношению друг к другу и рассматриваемому объекту, а не к типу объекта внутри и от него.

Это может помочь мышление проблемы в контексте безличного языка, потому что это не тот тип, который определяет, является ли это "квадрат", а фактические свойства объекта, который определяет, является ли это "квадратом".

Я думаю, если вы хотите еще больше абстрагироваться, вы даже не скажете, что у вас есть четырехугольник, но у вас есть многоугольник или даже просто форма.

Ответ 8

Его довольно просто:) Чем больше "базовый" класс (первый в цепочке деривации) должен быть самым общим.

Например, shape → Rectangle → Square.

Здесь квадрат - это особый случай прямоугольника (с ограниченными размерами), а прямоугольник - особый случай формы.

Говорите другим способом - используйте тест "есть". Сквайр - это прямоугольник. Но прямоугольник не всегда является квадратом.