Мне не всегда нравится Лисков, поскольку он, кажется, ограничивает возможности наследования на основе поведения, а не сущности. На мой взгляд, наследование всегда должно было быть отношением, а не действовать точно так же.
При этом статья в Википедии подробно описывает почему некоторые считают это плохим, используя ваш точный пример:
Типичным примером, нарушающим LSP, является класс Square, производный от класса Rectangle, при условии, что методы получения и установки существуют как для ширины, так и для высоты.
Класс Square всегда предполагает, что ширина равна высоте. Если объект Square используется в контексте, где ожидается объект Rectangle, может возникнуть неожиданное поведение, поскольку размеры объекта Square не могут (или, скорее, не должны) изменяться независимо.
Эту проблему нелегко решить: если мы сможем модифицировать методы установки в классе Square так, чтобы они сохраняли инвариант Square (т. е. сохраняли одинаковые размеры), то эти методы ослабят (нарушат) постусловия для установок Rectangle, которые утверждают, что размеры могут быть изменены независимо.
Итак, глядя на ваш код вместе с эквивалентным кодом Rectangle
:
s = Square.new(100) r = Rectangle.new(100,100)
s.width = 50 r.width = 50
puts s.height puts r.height
вывод будет 50 слева и 100 справа.
Но, это, на мой взгляд, важная часть статьи:
Нарушения LSP, подобные этому, могут или не могут быть проблемой на практике, в зависимости от постусловий или инвариантов, которые фактически ожидаются кодом, использующим классы, нарушающие LSP.
Другими словами, при условии, что код, использующий классы, понимает поведение, проблем не возникает.
Суть в том, что квадрат является правильным подмножеством прямоугольника для достаточно свободного определения прямоугольника :-)
person
paxdiablo
schedule
29.05.2013