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

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

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

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

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

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

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

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

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

 Rectangle rect = new Square(20); // both width and height SetWidth(rect, 100); Assert.AreEqual(20, rect.Height); 

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

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

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

редактировать

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

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

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

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

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

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

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

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

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

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

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

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

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

Проблема в том, что описываемое на самом деле не является «типом», а совокупным эмерджентным свойством.

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

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

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

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

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

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

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

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

  • Лучше ли создать одноэлементный доступ к контейнеру единства или передать его через приложение?
  • C ++ RTTI жизнеспособные примеры
  • Действительно ли метатег видового экрана необходим?
  • Android «высота» не показывает тень
  • Стиль стилей для дизайна материалов для Android
  • Как установить текст для просмотра из макета заголовка ящика в навигационном ящике без раздувания
  • Откуда вы знаете, когда использовать шаблоны дизайна?
  • Почему многие регионы чувствительны к регистру?
  • CollapsingToolbarLayout setTitle () не обновляется, если не свернут
  • Почему Синглтон считается анти-шаблоном?
  • На панели инструментов нет тени по умолчанию?
  • Давайте будем гением компьютера.