Если Квадрат является типом Прямоугольника, то почему Квадрат не может наследовать от Прямоугольника? Или почему это плохой дизайн?
Я слышал, как люди говорят:
Если вы сделали Square производным от Rectangle, то Square должен использоваться везде, где вы ожидаете прямоугольник
В чем здесь проблема? И почему Square можно использовать везде, где вы ожидаете прямоугольник? Это будет полезно только в том случае, если мы создадим объект Square, и если мы переопределим методы SetWidth и SetHeight для Square, чем возникнет такая проблема?
Если у вас есть методы SetWidth и SetHeight в базовом классе Rectangle, и если ваша ссылка Rectangle указывает на квадрат, то SetWidth и SetHeight не имеют смысла, поскольку установка одного из них приведет к изменению другого в соответствии с ним. В этом случае Square не проходит тест подстановки Лискова с помощью Rectangle, и абстракция наличия наследования Square от Rectangle является плохой.
Может кто-нибудь объяснить приведенные выше аргументы? Опять же, если мы переопределим методы SetWidth и SetHeight в Square, не решит ли это эту проблему?
Я также слышал / читал:
Реальная проблема заключается в том, что мы моделируем не прямоугольники, а скорее «изменяемые прямоугольники», то есть прямоугольники, ширину или высоту которых можно изменить после создания (и мы по-прежнему считаем, что это один и тот же объект). Если мы посмотрим на класс прямоугольника таким образом, то станет ясно, что квадрат не является «изменяемым прямоугольником», потому что квадрат не может быть изменен и все же будет квадратом (в общем). Математически мы не видим проблемы, потому что изменчивость даже не имеет смысла в математическом контексте
Здесь я полагаю, что «изменяемый размер» является правильным термином. Прямоугольники «изменяемого размера» и квадраты. Я что-то упустил в приведенном выше аргументе? Квадрат можно изменить, как любой прямоугольник.
источник
Why do we even need Square
? Это как две ручки. Одна синяя ручка и одна красная синяя, желтая или зеленая ручка. Синяя ручка избыточна - тем более в случае с квадратом, поскольку она не имеет экономической выгоды.Ответы:
По сути, мы хотим, чтобы все было разумно.
Рассмотрим следующую проблему:
Мне дали группу прямоугольников, и я хочу увеличить их площадь на 10%. Поэтому я устанавливаю длину прямоугольника в 1,1 раза больше, чем была раньше.
Теперь в этом случае длина всех моих прямоугольников теперь увеличена на 10%, что увеличит их площадь на 10%. К сожалению, кто-то фактически передал мне смесь квадратов и прямоугольников, и когда длина прямоугольника была изменена, изменилась и ширина.
Мои модульные тесты пройдены, потому что я написал все свои модульные тесты, чтобы использовать набор прямоугольников. Теперь я внес небольшую ошибку в свое приложение, которая может оставаться незамеченной в течение нескольких месяцев.
Хуже того, Джим из бухгалтерии видит мой метод и пишет какой-то другой код, который использует тот факт, что если он передаст квадраты моему методу, он получит очень хорошее увеличение размера на 21%. Джим счастлив, и никто не мудрее.
Джим получил повышение за отличную работу в другом отделе. Альфред присоединяется к компании как младший. В своем первом сообщении об ошибке Джилл из рекламного агентства сообщил, что пропуск квадратов для этого метода приводит к увеличению на 21% и требует исправления ошибки. Альфред видит, что квадраты и прямоугольники используются повсюду в коде, и понимает, что разрыв цепочки наследования невозможен. Он также не имеет доступа к исходному коду учета. Таким образом, Альфред исправляет ошибку следующим образом:
Альфред доволен своими навыками хакерского убера, и Джилл подписывает, что ошибка исправлена.
В следующем месяце никому не платят, потому что бухгалтерия зависела от того, можно ли передать квадраты
IncreaseRectangleSizeByTenPercent
методу и получить увеличение на 21%. Вся компания переходит в режим «исправление приоритета 1», чтобы отследить источник проблемы. Они прослеживают проблему до решения Альфреда. Они знают, что им нужно поддерживать и бухгалтерию, и рекламу. Таким образом, они решают проблему, идентифицируя пользователя с помощью вызова метода следующим образом:И так далее.
Этот анекдот основан на реальных ситуациях, с которыми ежедневно сталкиваются программисты. Нарушение принципа подстановки Лискова может привести к очень тонким ошибкам, которые обнаруживаются только спустя годы после того, как они написаны, и к тому времени, когда исправление нарушения нарушит кучу вещей, а не исправление его разозлит вашего крупнейшего клиента.
Есть два реалистичных способа решения этой проблемы.
Первый способ - сделать Rectangle неизменным. Если пользователь Rectangle не может изменить свойства длины и ширины, эта проблема исчезнет. Если вы хотите прямоугольник с другой длиной и шириной, вы создаете новый. Квадраты могут наследовать от прямоугольников счастливо.
Второй способ - разорвать цепочку наследования между квадратами и прямоугольниками. Если квадрат определяются как имеющие единственной
SideLength
собственностью и прямоугольники имеютLength
иWidth
имущество , и нет наследства, то невозможно случайно сломать вещи, ожидая прямоугольник и получить квадрат. В терминах C # вы можете использоватьseal
класс прямоугольников, который гарантирует, что все прямоугольники, которые вы когда-либо получали, на самом деле являются прямоугольниками.В этом случае мне нравится способ решения проблемы «неизменяемыми объектами». Идентичность прямоугольника - это его длина и ширина. Имеет смысл, что когда вы хотите изменить идентичность объекта, то, что вы действительно хотите, это новый объект. Если вы потеряете старого клиента и получите нового, вы не меняете
Customer.Id
поле со старого клиента на нового, вы создаете новогоCustomer
.Нарушения принципа подстановки Лискова часто встречаются в реальном мире, в основном потому, что много кода написано людьми, которые некомпетентны / испытывают нехватку времени / не заботятся / делают ошибки. Это может и действительно приводит к некоторым очень неприятным проблемам. В большинстве случаев вы предпочитаете композицию вместо наследования .
источник
Если все ваши объекты неизменны, проблем нет. Каждый квадрат также является прямоугольником. Все свойства прямоугольника также являются свойствами квадрата.
Проблема начинается, когда вы добавляете возможность изменять объекты. Или действительно - когда вы начинаете передавать аргументы объекту, а не просто читать свойства getter.
Существуют модификации, которые вы можете сделать для Rectangle, которые поддерживают все инварианты вашего класса Rectangle, но не все квадратные инварианты - например, изменение ширины или высоты. Внезапно поведение Rectangle - это не только его свойства, но и возможные модификации. Это не только то, что вы получаете из прямоугольника, это также то, что вы можете вставить .
Если у вашего Rectangle есть метод,
setWidth
который задокументирован как изменение ширины и не изменение высоты, то у Square не может быть совместимого метода. Если вы измените ширину, а не высоту, результат больше не будет действительным квадратом. Если вы решили изменить ширину и высоту квадрата при использованииsetWidth
, вы не реализуете спецификацию RectanglesetWidth
. Ты просто не можешь победить.Когда вы посмотрите на то, что вы можете «поместить» в Прямоугольник и Квадрат, какие сообщения вы можете им отправить, вы, вероятно, обнаружите, что любое сообщение, которое вы можете правильно отправить на Квадрат, вы также можете отправить в Прямоугольник.
Это вопрос совмещения и противоречия.
Методы надлежащего подкласса, в котором экземпляры могут использоваться во всех случаях, когда ожидается наличие суперкласса, требуют, чтобы каждый метод:
Итак, вернемся к Rectangle и Square: может ли Square быть подклассом Rectangle, полностью зависит от того, какие методы есть у Rectangle.
Если Rectangle имеет отдельные сеттеры для ширины и высоты, Square не будет хорошим подклассом.
Аналогично, если вы сделаете несколько методов в вариантах аргументов, например,
compareTo(Rectangle)
для Rectangle иcompareTo(Square)
Square, у вас будет проблема с использованием Square в качестве Rectangle.Если вы сделаете так, чтобы ваши Square и Rectangle были совместимыми, они, скорее всего, будут работать, но они должны разрабатываться вместе, или я готов поспорить, что они не будут работать.
источник
Здесь много хороших ответов; Ответ Стивена, в частности, хорошо иллюстрирует, почему нарушения принципа замещения приводят к реальным конфликтам между командами.
Я подумал, что мог бы вкратце поговорить о конкретной проблеме прямоугольников и квадратов, а не использовать ее в качестве метафоры для других нарушений LSP.
Существует дополнительная проблема с прямоугольником, представляющим собой особый вид прямоугольника, о котором редко упоминают, а именно: почему мы останавливаемся с квадратами и прямоугольниками ? Если мы хотим сказать, что квадрат - это особый вид прямоугольника, то, конечно же, мы должны также сказать:
Что на земле все отношения должны быть здесь? Языки на основе наследования классов, такие как C # или Java, не были предназначены для представления таких сложных отношений с множеством различных видов ограничений. Лучше всего просто избегать вопроса, не пытаясь представить все эти вещи как классы с подтиповыми отношениями.
источник
IShape
тип, который включает в себя ограничивающий прямоугольник, его можно рисовать, масштабировать и сериализовать, а также иметьIPolygon
подтип с методом для сообщения количества вершин и методом для возвратаIEnumerable<Point>
. Тогда можно было бы иметьIQuadrilateral
подтип, который происходит изIPolygon
этогоIRhombus
иIRectangle
происходит из этого, а такжеISquare
изIRhombus
иIRectangle
. Изменчивость отбрасывает все в окно, а множественное наследование не работает с классами, но я думаю, что это нормально с неизменяемыми интерфейсами.IRhombus
гарантируется, что всеPoint
возвращаемые изEnumerable<Point>
заданного значенияIPolygon
соответствуют ребрам равной длины? Поскольку реализация одного толькоIRhombus
интерфейса не гарантирует, что конкретный объект является ромбом, наследование не может быть ответом.С математической точки зрения квадрат - это прямоугольник. Если математик изменяет квадрат, чтобы он больше не придерживался квадратного контракта, он превращается в прямоугольник.
Но в ОО-дизайне это проблема. Объект - это то, чем он является, и это включает в себя как поведение, так и состояние. Если я держу квадратный объект, но кто-то другой изменяет его, чтобы он был прямоугольником, это нарушает контракт квадрата не по моей вине. Это вызывает все виды плохих вещей.
Ключевым фактором здесь является изменчивость . Может ли форма измениться после ее построения?
Изменчивый: если формы могут изменяться после создания, квадрат не может иметь отношения is-a с прямоугольником. Контракт прямоугольника включает в себя ограничение на то, что противоположные стороны должны быть равной длины, но смежные стороны не должны быть. Квадрат должен иметь четыре равные стороны. Изменение квадрата через интерфейс прямоугольника может нарушить контракт квадрата.
Неизменный: если формы не могут измениться после создания, то квадратный объект также всегда должен выполнять контракт прямоугольника. Квадрат может иметь отношение is-a с прямоугольником.
В обоих случаях можно попросить квадрат создать новую форму на основе его состояния с одним или несколькими изменениями. Например, можно сказать «создайте новый прямоугольник на основе этого квадрата, за исключением того, что противоположные стороны A и C в два раза длиннее». Поскольку новый объект строится, первоначальный квадрат продолжает придерживаться своих контрактов.
источник
This is one of those cases where the real world is not able to be modeled in a computer 100%
, Почему так? У нас все еще может быть функциональная модель квадрата и прямоугольника. Единственное последствие - мы должны искать более простую конструкцию для абстрагирования над этими двумя объектами.Потому что это часть того, что означает подтип (см. Также: принцип подстановки Лискова). Вы можете сделать, должны быть в состоянии сделать это:
Вы фактически делаете это все время (иногда даже неявно) при использовании ООП.
Потому что вы не можете разумно переопределить их
Square
. Потому что квадрат не может быть «изменен по размеру как любой прямоугольник». Когда высота прямоугольника изменяется, ширина остается неизменной. Но когда высота квадрата изменяется, ширина должна измениться соответственно. Проблема не только в том, что ее можно изменить, но и в обоих измерениях независимо друг от друга.источник
Rect r = s;
строка, вы можете просто,doSomethingWith(s)
и среда выполнения будет использовать любые вызовыs
для разрешения любых виртуальныхSquare
методов.setWidth
иsetHeight
измените ширину и высоту.То, что вы описываете, противоречит принципу подстановки Лискова . Основная идея LSP заключается в том, что всякий раз, когда вы используете экземпляр определенного класса, вы всегда должны иметь возможность заменять экземпляр любого подкласса этого класса, не внося ошибок.
Проблема прямоугольника и квадрата не очень хороший способ представить Лискова. Он пытается объяснить широкий принцип, используя пример, который на самом деле довольно тонкий, и идет вразрез с одним из наиболее распространенных интуитивных определений во всей математике. Некоторые называют это проблемой Эллипс-Круга по этой причине, но это только немного лучше, насколько это идет. Лучше всего сделать небольшой шаг назад, используя то, что я называю проблемой Parallelogram-Rectangle. Это делает вещи намного проще для понимания.
Параллелограмм - это четырехугольник с двумя парами параллельных сторон. У этого также есть две пары конгруэнтных углов. Нетрудно представить объект Parallelogram по этим направлениям:
Один общий способ думать о прямоугольнике - это параллелограмм с прямыми углами. На первый взгляд может показаться, что Rectangle является хорошим кандидатом для наследования от Parallelogram , так что вы можете повторно использовать весь этот вкусный код. Тем не мение:
Почему эти две функции вводят ошибки в Rectangle? Проблема в том, что вы не можете изменить углы в прямоугольнике : они определены как всегда равными 90 градусам, и поэтому этот интерфейс фактически не работает для Rectangle, наследуемого от Parallelogram. Если я поменяю Rectangle на код, который ожидает параллелограмм, и этот код попытается изменить угол, почти наверняка будут ошибки. Мы взяли что-то, что было доступно для записи в подклассе, и сделали его доступным только для чтения, и это нарушение Лискова.
Теперь, как это применимо к квадратам и прямоугольникам?
Когда мы говорим, что вы можете установить значение, мы обычно имеем в виду нечто более сильное, чем просто возможность записать значение в него. Мы подразумеваем определенную степень исключительности: если вы установите значение, а затем исключите некоторые чрезвычайные обстоятельства, оно останется на этом уровне, пока вы не установите его снова. Существует множество вариантов использования значений, в которые можно записывать значения, но они не остаются установленными, но также существует много случаев, которые зависят от значения, которое остается на месте после его установки. И вот тут мы сталкиваемся с другой проблемой.
Наш класс Square унаследовал ошибки от Rectangle, но у него есть некоторые новые. Проблема с setSideA и setSideB заключается в том, что ни один из них больше не является действительно устанавливаемым: вы все равно можете записать значение в любой из них, но оно изменится из-под вас, если записать другое. Если я поменяю местами параллелограмм в коде, который зависит от возможности устанавливать стороны независимо друг от друга, он будет в шоке.
Вот в чем проблема, и именно поэтому есть проблема с использованием Rectangle-Square в качестве введения в Liskov. Прямоугольник-квадрат зависит от разницы между способностью записывать что-либо и возможностью установить его, и это гораздо более тонкая разница, чем возможность задавать что-то по сравнению с тем, чтобы это было доступно только для чтения. Прямоугольник-квадрат все еще имеет значение в качестве примера, потому что он документирует довольно распространенную ошибку, за которой нужно следить, но ее не следует использовать в качестве вводного примера. Позвольте учащемуся сначала получить базовые знания, а затем бросить в них что-то более сложное.
источник
Подтип о поведении.
Чтобы тип
B
был подтипом типаA
, он должен поддерживать каждую операцию,A
поддерживаемую этим типом, с одной и той же семантикой (причудливый разговор о «поведении»). Используя обоснование, что каждый B является A , не работает - совместимость поведения имеет решающее значение. Большую часть времени "B является разновидностью A" совпадает с "B ведет себя как A", но не всегда .Пример:
Рассмотрим множество действительных чисел. В любом языке, мы можем ожидать , что они поддерживают операции
+
,-
,*
и/
. Теперь рассмотрим множество натуральных чисел ({1, 2, 3, ...}). Понятно, что каждое положительное целое число также является действительным числом. Но является ли тип натуральных чисел подтипом типа действительных чисел? Давайте посмотрим на четыре операции и посмотрим, ведут ли положительные целые числа так же, как действительные числа:+
: Мы можем добавить положительные целые числа без проблем.-
: Не все вычитания натуральных чисел приводят к натуральным числам. Например3 - 5
.*
: Мы можем умножать натуральные числа без проблем./
: Мы не можем всегда делить положительные целые числа и получать положительное целое число. Например5 / 3
.Таким образом, несмотря на то, что натуральные числа являются подмножеством действительных чисел, они не являются подтипами. Аналогичный аргумент может быть сделан для целых чисел конечного размера. Очевидно, что каждое 32-разрядное целое число также является 64-разрядным целым числом, но оно
32_BIT_MAX + 1
даст вам разные результаты для каждого типа. Так что, если я дал вам какую-то программу, и вы изменили тип каждой 32-битной целочисленной переменной на 64-битное целое, есть большая вероятность, что программа будет вести себя по-разному (что почти всегда означает, что неправильно ).Конечно, вы можете определить
+
32-битные числа, чтобы получить 64-разрядное целое число, но теперь вам придется резервировать 64-битное пространство каждый раз, когда вы добавляете два 32-разрядных числа. Это может или не может быть приемлемым для вас в зависимости от ваших потребностей памяти.Почему это важно?
Важно, чтобы программы были правильными. Это, пожалуй, самое важное свойство для программы. Если программа верна для некоторого типа
A
, единственный способ гарантировать, что программа продолжит быть верным для некоторого подтипа,B
- это если вестиB
себя какA
во всех отношениях.Таким образом, у вас есть тип
Rectangles
, спецификация которого гласит, что его стороны могут быть изменены независимо. Вы написали несколько программ, которые используютRectangles
и предполагают, что реализация соответствует спецификации. Затем вы ввели подтип,Square
чьи стороны не могут быть изменены независимо. В результате большинство программ, изменяющих размер прямоугольников, теперь будут ошибаться.источник
Перво-наперво, спросите себя, почему вы думаете, что квадрат - это прямоугольник.
Конечно, большинство людей узнали об этом в начальной школе, и это казалось бы очевидным. Прямоугольник - это четырехсторонняя фигура с углами 90 градусов, и квадрат отвечает всем этим свойствам. Так разве квадрат не является прямоугольником?
Дело в том, что все зависит от того, каковы ваши первоначальные критерии для группировки объектов, в каком контексте вы смотрите на эти объекты. В геометрии фигуры классифицируются на основе свойств их точек, линий и ангелов.
Поэтому, прежде чем вы скажете «квадрат - это тип прямоугольника», вы должны сначала спросить себя, основано ли это на критериях, которые меня интересуют .
В подавляющем большинстве случаев это совсем не то, что вас волнует. Большинство систем, которые моделируют фигуры, такие как графические интерфейсы, графика и видеоигры, в первую очередь касаются не геометрической группировки объекта, а поведения. Вы когда-нибудь работали над системой, которая имела значение, что квадрат был типом прямоугольника в геометрическом смысле. Что бы это даже дало вам, зная, что у него 4 стороны и 90 градусов?
Вы не моделируете статическую систему, вы моделируете динамическую систему, в которой что-то должно произойти (формы будут создаваться, разрушаться, изменяться, рисоваться и т. Д.). В этом контексте вы заботитесь о совместном поведении между объектами, потому что ваша главная задача - то, что вы можете сделать с формой, какие правила необходимо соблюдать, чтобы по-прежнему иметь целостную систему.
В этом контексте квадрат определенно не является прямоугольником , потому что правила, которые определяют, как квадрат можно изменить, не совпадают с прямоугольником. Так что они не одно и то же.
В этом случае не моделируйте их как таковые. Почему ты? Это не приносит вам ничего, кроме ненужных ограничений.
Если вы делаете это, хотя вы практически заявляете в коде, что это не одно и то же. Ваш код сказал бы, что квадрат ведет себя так, а прямоугольник ведет себя так, но они все те же.
Они явно не совпадают в контексте, о котором вы заботитесь, потому что вы только что определили два разных поведения. Так зачем делать вид, что они одинаковые, если они похожи только в контексте, который вас не волнует?
Это подчеркивает серьезную проблему, когда разработчики приходят в область, которую они хотят моделировать. Очень важно уточнить, в каком контексте вы заинтересованы, прежде чем начать думать об объектах в домене. Какой аспект вас интересует. Тысячи лет назад греки заботились об общих свойствах линий и ангелов форм и группировали их на основании этих данных. Это не означает, что вы вынуждены продолжать эту группировку, если это не то, что вас волнует (что в 99% случаев моделирования в программном обеспечении вас не волнует).
Многие ответы на этот вопрос сосредоточены на подтипах, касающихся группового поведения , потому что они являются правилами .
Но так важно понимать, что вы делаете это не просто для того, чтобы следовать правилам. Вы делаете это, потому что в подавляющем большинстве случаев это то, что вас действительно волнует. Вам все равно, если квадрат и прямоугольник имеют одинаковые внутренние ангелы. Вы заботитесь о том, что они могут сделать, оставаясь при этом квадратами и прямоугольниками. Вы заботитесь о поведении объектов, потому что вы моделируете систему, которая фокусируется на изменении системы на основе правил поведения объектов.
источник
Rectangle
используются только для представления значений , класс может иметь возможностьSquare
наследоватьRectangle
и полностью соблюдать свой контракт. К сожалению, многие языки не делают различий между переменными, которые инкапсулируют значения, и переменными, которые идентифицируют сущности.Square
типа, который наследуется от неизменяемогоRectnagle
типа, может быть полезным, если существуют некоторые виды операций, которые можно выполнять только над квадратами. В качестве реалистичного примера концепции рассмотримReadableMatrix
тип [базовый тип, прямоугольный массив, который может храниться различными способами, в том числе редко), иComputeDeterminant
метод. Возможно, имеет смыслComputeDeterminant
работать только сReadableSquareMatrix
типом, производным отReadableMatrix
которого, я бы посчитал примеромSquare
производного от aRectangle
.Проблема заключается в том, чтобы думать, что если вещи в реальности связаны каким-то образом, то после моделирования они должны быть точно такими же.
Самым важным в моделировании является определение общих атрибутов и общих поведений, определение их в базовом классе и добавление дополнительных атрибутов в дочерние классы.
Проблема с вашим примером в том, что это абсолютно абстрактно. Пока никто не знает, для чего вы планируете использовать эти классы, трудно угадать, какой дизайн вы должны сделать. Но если вы действительно хотите иметь только высоту, ширину и размер, было бы логичнее:
width
параметром иresize(double factor)
изменением ширины на заданный коэффициентheight
и переопределяет егоresize
функцию, которая вызывает,super.resize
а затем изменяет размер высоты с помощью заданного коэффициентаС точки зрения программирования, в Square нет ничего, чего нет у Rectangle. Нет смысла делать квадрат как подкласс Rectangle.
источник
Потому что с помощью LSP создание отношения наследования между двумя и переопределением,
setWidth
аsetHeight
также обеспечение того, что квадрат имеет как одно и то же, вводит в заблуждение и неинтуитивное поведение. Допустим, у нас есть код:Но если метод
createRectangle
вернулсяSquare
, потому что это возможно благодаряSquare
наследованию отRectange
. Тогда ожидания нарушаются. Здесь, с этим кодом, мы ожидаем, что установка ширины или высоты приведет только к изменению ширины или высоты соответственно. Смысл ООП состоит в том, что когда вы работаете с суперклассом, вы ничего не знаете о подклассе. И если подкласс изменяет поведение так, что оно идет вразрез с ожиданиями, которые мы имеем в отношении суперкласса, то существует высокая вероятность возникновения ошибок. И такие ошибки трудно отлаживать и исправлять.Одна из основных идей ООП заключается в том, что это поведение, а не данные, которые наследуются (что также является одним из основных заблуждений ИМО). И если вы посмотрите на квадрат и прямоугольник, они сами не имеют поведения, которое мы могли бы связать в отношении наследования.
источник
LSP говорит, что все, что наследуется,
Rectangle
должно бытьRectangle
. То есть он должен делать все, чтоRectangle
делает.Вероятно, в документации
Rectangle
написано, что поведениеRectangle
именованногоr
выглядит следующим образом:Если ваш Квадрат не имеет такого поведения, то он не ведет себя как
Rectangle
. Таким образом, LSP говорит, что это не должно наследоваться отRectangle
. Язык не может применить это правило, потому что он не может помешать вам сделать что-то неправильно в переопределении метода, но это не значит, что «все в порядке, потому что язык позволяет мне переопределять методы» - это убедительный аргумент для этого!Теперь, это было бы возможно , чтобы написать документацию
Rectangle
таким образом , что это не означает , что приведенный выше код печатает 10, в этом случае , может быть , вашSquare
может бытьRectangle
. Вы можете увидеть документацию, в которой говорится что-то вроде: «это делает X. Более того, реализация в этом классе делает Y». Если так, то у вас есть хороший пример для извлечения интерфейса из класса и разграничения того, что интерфейс гарантирует, и того, что класс гарантирует в дополнение к этому. Но когда люди говорят, что «изменяемый квадрат не является изменяемым прямоугольником, тогда как неизменный квадрат является неизменяемым прямоугольником», они в основном предполагают, что приведенное выше действительно является частью разумного определения изменяемого прямоугольника.источник
Подтипы и, соответственно, ОО-программирование часто полагаются на Принцип замещения Лискова, согласно которому любое значение типа А может использоваться там, где требуется Б, если А <= В. Это в значительной степени аксиома в ОО-архитектуре, т.е. предполагается, что все подклассы будут иметь это свойство (и если нет, то подтипы ошибочны и должны быть исправлены).
Однако оказывается, что этот принцип либо нереалистичен / непредставителен для большей части кода, либо действительно невозможен для выполнения (в нетривиальных случаях)! Эта проблема, известная как проблема прямоугольника или прямоугольника ( http://en.wikipedia.org/wiki/Circle-ellipse_problem ), является одним из известных примеров того, насколько трудно ее решить .
Обратите внимание , что мы могли бы реализовать более-и-более наблюдательно-эквивалент квадратики и прямоугольники, но только отбросив все больше и больше функциональных возможностей, пока различие не имеет смысла.
В качестве примера см. Http://okmij.org/ftp/Computation/Subtyping/
источник