Получи случайную криптовалюту за регистрацию!

L is for Liskov — SOLID принципы знаете? Думаю, нет разработ | Java: fill the gaps

L is for Liskov

— SOLID принципы знаете?

Думаю, нет разработчика, который не слышал на собеседовании такой вопрос.

Расшифровку знают многие. А вот практические знания часто ограничиваются Single Responsibility и Interface Segregation.

Как применять остальные буквы в ежедневной работе — не всегда понятно. Больше всего вопросов возникает насчёт L — Liskov Substitution Principle. О нём и будет сегодняшний пост.

Полное определение звучит так:

Пусть q(x) является свойством верным относительно объектов x некоторого типа T. Тогда q(y) также должно быть верным для объектов y типа S, где S является подтипом типа T.

По-простому: если заменить класс А на подкласс B, то система будет работать корректно и без неожиданных сайд-эффектов.

A service1 = new A();
A service2 = new B();

Для наблюдателя service1 и service2 ведут себя совершенно одинаково. Класс-наследник дополняет поведение родителя, а не замещает его. В результате система работает более предсказуемо.

Как это выглядит на практике:

Выходной тип метода в наследнике такой же как у родителя или расширенный

Базовый класс: Info getInfo()

Наследник:
BigInfo getInfo()
Object getInfo()

Подклассы не бросают дополнительных исключений, но могут уменьшить их список

Базовый класс: void save() throws FileNotFoundException

Наследник:
void save()
void save() throws FileNotFoundEx, InterruptedEx

Java — типизированный язык, поэтому пункты 1 и 2 контролируются компилятором.

Типы входных параметров те же или менее строгие. Пункт для общего понимания, тк для Java это неприменимо

Базовый класс: void add(Account acc)

Наследник:
void add(Object acc)
void add(AdminAccount acc)

Следующие пункты компилятор уже не проверит, это целиком ответственность программиста.

Метод подкласса делает то же, что и метод базового класса

Базовый класс: метод countVisitors считает пользователей

Наследник:
Считает пользователей чуть по-другому
Считает пользователей, обновляет статистику, сохраняет результат в БД

Метод наследника взаимодействует с теми же сущностями:
Метод родителя увеличивает счётчик - подкласс тоже увеличивает
Метод родителя не меняет поле - подкласс тоже не меняет
Метод родителя вызывает другие методы в определённом порядке - подкласс делает то же самое

А что можно вообще?

Если в подклассе объявлены новые поля, то методы подкласса могут делать с ними что угодно. На этом всё

Правила выше - очень строгие. Но и наследование — штука непростая, это самая сильная связь между сущностями. Часто единственный плюс — это краткость кода, но по ходу развития проекта ограничения доставляют всё больше проблем.

Нарушения принципа подстановки — повод пересмотреть иерархию наследования или совсем от неё отказаться.