Введение

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

Однако, проблема возникает не столько из-за ООП как такового, сколько из-за неправильной модели системы.

Принцип разделения интерфейса (Interface Segregation Principle, ISP) содержит правила и ограничения, которые помогают с этой проблемой справляться.

Принцип разделения интерфейса

Сущности не должны зависеть от интерфейсов, которые они не используют.

Когда принцип нарушается, модули подвержены всем изменениям в интерфейсах, от которых они зависят. Это приводит к высокой связанности модулей друг с другом.

ISP помогает проектировать интерфейсы так, чтобы изменения затрагивали только те модули, на функциональность которых они действительно влияют. Чаще всего это заставляет интерфейсы дробить (разделять).

Пример задачи

Допустим, требуется сделать программу, которая будет играть определённую ноту каждые несколько секунд. В наивном подходе с наследованием нам придётся построить нечто похожее на вот такую иерархию:

Структура, которая нарушает ISP

Проблема этой структуры в том, что мы представляем класс SoundEmitter потомком класса TimeInterval, хотя они друг с другом не связаны. Отсюда и возникает «необходимость» тащить ненужный код всем потомкам класса TimeInterval.

ISP предлагает два подхода к решению этой проблемы с помощью разделения интерфейсов: через делегирование и через множественное наследование.

Разделение через делегирование

Этот подход подразумевает использование шаблона проектирования под названием Адаптер.

Мы не будем связывать SoundEmitter и TimeInterval непосредственно друг с другом. Мы будем связывать их через дополнительный слой (адаптер), который будет транслировать сообщения от одной сущности другой.

Использование адаптера для связывания сущностей

Чуть подробнее об адаптере поговорим в разделе с шаблонами проектирования и приёмами рефакторинга.

Разделение через множественное наследование

Второй вариант предполагает, что IntervalSoundEmitter будет наследоваться одновременно от TimeInterval и от SoundEmitter. Это позволит отвязать родительские классы друг от друга и использовать в классе IntervalSoundEmitter только нужную функциональность.

Вариант структуры с множественным наследованием

Коротко

Принцип разделения интерфейса:

  • помогает бороться с наследованием или реализацией ненужной функциональности;
  • даёт возможность спроектировать модули так, чтобы их затрагивали изменения только тех интерфейсов, которые они действительно реализуют;
  • снижает зацепление модулей;
  • уничтожает наследование ради наследования, поощряет использование композиции;
  • позволяет выявлять более высокие абстракции и находить неочевидные связи между сущностями.

Материалы к разделу

Вопросы