Перейти к основному содержимому

Держи обещание

Принцип подстановки Лисков говорит: везде, где код ожидает интерфейс, каждая реализация этого интерфейса должна работать правильно. Реализация, которая формально подходит, но ведёт себя плохо — выбрасывает ошибку, ничего не делает, обманывает — ломает каждую функцию, которая доверилась интерфейсу.

Интерфейс — это обещание. fly() обещает полёт. Класс, который реализует fly() через падение с ошибкой, подписал контракт, который не может выполнить.


Замена, которая нарушает договор

Интерфейс Bird обещает fly(). Воробьи обещание выполняют. Пингвины — тоже птицы, поэтому кажется естественным реализовать интерфейс... выбросив ошибку, ведь пингвины не умеют летать.

Теперь airShow, который работает для каждой честной птицы, падает на пингвине. Ошибка не в airShow — она в замене, которая нарушила обещание.

Пингвин, который срывает шоу

airShow доверяет интерфейсу и вызывает fly() у каждой птицы. Воробей выступает; пингвин выбрасывает ошибку и прямо там останавливает шоу (try/catch лишь смягчает падение, чтобы показать сообщение).

Prompt
Напиши программу на TypeScript, показывающую нарушение принципа подстановки Лисков: интерфейс Bird с методом fly, класс Sparrow, который летает, и класс Penguin, у которого fly выбрасывает ошибку, потому что пингвины не умеют летать. Функция airShow вызывает fly у каждой птицы; оберни шоу в try/catch, чтобы напечатать сообщение об аварии, когда выступает пингвин.
Вывод: программа на TypeScript и захваченный вывод

Раздели обещания

Решение: обещать только то, что правда. Разделим раздутое обещание на Flyer и Swimmer. Воробей реализует Flyer, пингвин реализует Swimmer — и ни один класс не подписывает контракт, который не может выполнить.

Самое приятное — кто теперь ловит ошибку: передача пингвина в airShow больше не падение во время работы программы, а ошибка типа, о которой сообщат ещё до запуска кода.

Каждый тип держит своё обещание

airShow принимает Flyer, poolShow принимает Swimmer. Каждое животное появляется только там, где его обещание выполняется — подстановка безопасна по построению.

Prompt
Исправь нарушение принципа Лисков в TypeScript, разделив обещания: интерфейс Flyer с методом fly и интерфейс Swimmer с методом swim. Sparrow реализует Flyer, Penguin реализует Swimmer. Напиши airShow для летающих и poolShow для плавающих и запусти обе функции, чтобы каждый класс появлялся только там, где его обещание выполняется.
Вывод: программа на TypeScript и захваченный вывод
Every type keeps its promise

Любой Flyer может присоединиться

Выгода честных интерфейсов: новый класс Eagle присоединяется к авиашоу без всякого риска — всё, что реализует Flyer, действительно летает, поэтому airShow никогда не нужно сомневаться во входных данных.

Prompt
Расширь пример с интерфейсом Flyer на TypeScript новым классом Eagle и проведи авиашоу с несколькими летающими объектами, показав, что любая честная реализация Flyer может безопасно подставляться.
Вывод: программа на TypeScript и захваченный вывод