Вопрос 1: Если у нас есть отношение суперкласс / подкласс, почему мы хотим объявить экземпляр как супертип, но создать его экземпляр (новый) как подтип?
Причины, по которым вы бы делали это с супертипом, - это те же причины, по которым вы делаете это с интерфейсом.Все перечисленные вами причины того, почему объявление переменной как ее определенного подтипа, а не супертипа, применимы в равной степени и к тому, почему объявление переменной как ее конкретного подтипа, а не интерфейса, который реализует этот подтип.
abstract class Car { ... }
public abstract class ToyotaCamery2011 extends Car ( ... )
class Garage {
private Car car = new ToyotaCamery2011();
public Car getCar() { return car; }
....
}
class Garage {
private ToyotaCamery2011 toyotaCamery2011 = new ToyotaCamery2011();
public Car getCar() { return toyotaCamery2011; }
....
}
AsПока все методы Garage
используют только методы Car
, а открытый интерфейс Garage
показывает только Car
и ничего специфичного для Prius2011
, эти 2 класса фактически эквивалентны.Что легче понять, например, какой из них более точно моделирует реальный мир?Что гарантирует, что я случайно не использую специфичный для Prius метод, то есть построил специфичный для Prius гараж?Что является чуть-чуть более легким в обслуживании, если я решу купить новую машину?Улучшен ли код каким-либо образом с использованием определенного подтипа?
Вопрос 2: Так почему или как этот пример кода нарушает LSP?Только из-за того, что инвариант квадрата всех сторон равен, нарушает инвариант прямоугольника, что стороны могут быть изменены независимо?Если это причина, то нарушение LSP будет только теоретическим?Или как в коде я мог видеть, как этот код нарушает принцип?
Трудно говорить о LSP, не говоря об обещаниях / контрактах.Но да, если Rectangle
обещает, что стороны могут быть изменены независимо (более формально, если постусловие для вызова Rectangle.setWidth()
включает, что Rectangle.getHeight () не должно быть затронуто), то Square
, полученное из Rectangle
, нарушает LSP.
Ваша программа не зависит от этого свойства, поэтому все в порядке.Однако возьмите программу, которая пытается удовлетворить значение периметра или площадь.Такая программа может опираться на идею, что Rectangle
имеет независимые стороны.
Любой класс, который принимает Rectangle
в качестве входных данных и зависит от этого свойства / поведения Rectangle
, вероятно, сломается, если ему присваивается Square
в качестве ввода.Программы, подобные этой, могут либо перепрыгивать через обручи, чтобы искать и запрещать Square
(который является знанием подкласса), либо могут изменять контракт Rectangle
относительно независимых размеров.Тогда все программы, использующие Rectangle
, могут проверять после каждого вызова setWidth()
или setLength () to see whether the adjacent side also changed and react accordingly. If it does the latter, than
Square deriving frmo
Rectangle` больше не является нарушением LSP.
Это не просто теоретический,это может оказать реальное влияние на программное обеспечение, но на практике оно часто подвергается риску.Вы видите это на Java часто, к сожалению.Класс Iterator
в Java предоставляет необязательный метод remove()
.Классы, использующие итератор, должны знать о реализующем классе и / или его подклассах, чтобы знать, безопасно ли его использовать Iterator.remove()
.Это нарушает LSP, но это общепринятая практика на Java.Это делает написание и обслуживание программного обеспечения более сложным и более восприимчивым к ошибкам.
Вопрос 3: Принцип Open-Closed гласит, что мы должны вводить новое поведение / функциональность через новые классы (наследование или интерфейсы).Так, если, например, у меня есть метод WriteLog в базовом классе, который не имеет предварительных условий, но я ввожу новый подкласс, который переопределяет метод, но ТОЛЬКО фактически записывает в журнал, если событие очень критично ... если этоновая предполагаемая функциональность (предварительное условие ужесточается для подтипа), это все еще будет нарушать LSP?В этом случае два принципа могут противоречить друг другу.
Я думаю, что вы подразумеваете постусловия, когда говорите предусловия - вы описываете то, что метод обещает выполнить. Если так, то я не вижу нарушения LSP - если суперкласс метода ничего не обещает, тогда подкласс может делать то, что ему нравится, и при этом быть полностью заменяемым. Тот факт, что подкласс является более избирательным («ТОЛЬКО фактически пишет») о том, что он пишет, является новой функциональностью, особенно в свете того факта, что суперкласс ничего не обещает.