Принцип разделения интерфейсов
Принцип разделения интерфейсов (Interface Segregation Principle) гласит что: «Ни один клиент не должен зависеть от методов, которые он не использует». Принцип разделения интерфейсов был предложен Робертом К. Мартином, когда он консультировал компанию Xerox. Принцип разделения интерфейсов предполагает создание небольших интерфейсов, известных как «ролевые интерфейсы», вместо большого интерфейса, состоящего из нескольких методов. Разделяя методы по ролям на более мелкие интерфейсы, клиенты будут зависеть только от методов, которые имеют к ним отношение. Допустим, мы разрабатываем приложение для различных коммуникационных устройств. Мы говорим, что устройство связи – это устройство, которое будет иметь одну или несколько из следующих функций: совершать звонки, отправлять SMS или искать в Интернете. Итак, мы создаем интерфейс с именем CommunicationDevice и добавляем соответствующие абстрактные методы для каждой из этих функций, чтобы любой создаваемый класс смог реализовать эти методы. Затем мы создаем класс SmartPhone с помощью интерфейса CommunicationDevice и реализуем функционал абстрактных методов. До сих пор все было в порядке. Теперь предположим, что нам нужно создать стационарный телефон. Он тоже является устройством связи, поэтому мы создаем новый класс LandlinePhone через тот же интерфейс CommunicationDevice. Именно здесь мы сталкиваемся с проблемой из-за объемного интерфейса CommunicationDevice. В классе LandlinePhone мы реализовываем метод make_calls(), но поскольку мы также наследуем абстрактные методы send_sms() и browse_internet(), мы должны предоставить реализацию и этих двух абстрактных методов в классе LandlinePhone, даже если они в принципе неприменимы к этому виду телефонов. Мы можем либо создать исключение, либо оставить pass вместо реализации, но нам все равно нужно ее предоставить.
Все можно исправить, следуя принципу разделения интерфейсов, как в примере ниже. Вместо создания большого интерфейса мы создаем более маленькие ролевые интерфейсы для каждого метода. Соответствующие классы будут использовать только связанные интерфейсы.
Задачи
Задача 1. Принтер и сканер
Интерфейс MultifunctionDevice содержит методы print(), scan() и fax(). Класс SimplePrinter реализует его, но не поддерживает сканирование и факс. Нарушает ли это ISP? Разбейте интерфейс.
Задача 2. Работник и робот
Интерфейс Worker содержит методы work() и eat(). Класс Robot реализует его, но роботы не едят. Устраните нарушение ISP.
Задача 3. Репозиторий данных
Интерфейс Repository содержит методы find(), save(), delete() и export_csv(). Класс ReadOnlyRepository реализует только find(), а остальные методы бросают исключения. Разбейте интерфейс по ролям.
Задача 4. Медиаплеер
Интерфейс MediaPlayer содержит методы play_audio(), play_video() и show_subtitles(). Класс AudioPlayer поддерживает только аудио. Устраните нарушение ISP.
Задача 5. Платёжный шлюз
Интерфейс PaymentGateway содержит методы charge(), refund(), get_transaction_history() и generate_report(). Некоторые шлюзы не поддерживают возвраты или отчёты. Разбейте интерфейс по ролям и создайте класс BasicGateway, реализующий только оплату.