Что такое принцип разделения интерфейсов

Ответ

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

Пример:

// Плохо - один интерфейс для всего
interface Worker {
    fun work()
    fun eat()
}

// Лучше - разделенные интерфейсы
interface Workable {
    fun work()
}

interface Eatable {
    fun eat()
}

class Human : Workable, Eatable {
    override fun work() { /*...*/ }
    override fun eat() { /*...*/ }
}

class Robot : Workable {
    override fun work() { /*...*/ }
}

Преимущества:

  • Уменьшает связанность
  • Делает код более гибким
  • Упрощает поддержку
  • Позволяет избежать "пустых" реализаций методов

Ответ 18+ 🔞

Давай разберём этот принцип, а то некоторые архитекторы, ёпта, проектируют интерфейсы так, будто у них в голове манда с ушами.

Смотри, суть в чём. Представь, что ты нанимаешь работников. Есть интерфейс Worker. И в нём два метода: work() (работать) и eat() (жрать). Вроде логично для человека, да? Но потом ты нанимаешь робота. И что получается? Ты заставляешь бедного робота реализовывать метод eat(). Он что, масло будет пить или аккумуляторы грызть? Приходится писать пустую заглушку или вообще кидать исключение. Это пиздопроебибна чистой воды! Ты заставляешь сущность зависеть от того, что ей нахуй не нужно.

Вот поэтому принцип ISP и кричит: «Хватит это терпеть!». Не надо лепить один жирный интерфейс на все случаи жизни. Лучше сделай несколько точечных.

Как в примере: Workable для работы, Eatable для еды. Человек (Human) реализует оба — он и пашет, и в столовую ходит. А робот (Robot) реализует только Workable. И всем хорошо! Роботу не приходится притворяться, что он ест, а в коде не валяется методов-заглушек, от которых волнение ебать.

Какая от этого польза, спросишь?

  • Связанность уменьшается. Классы перестают цепляться за кучу ненужных им обязательств. Это как если бы тебе впаривали абонемент в спортзал, бассейн и на курсы макраме одним пакетом, а тебе только бассейн нужен.
  • Гибкость растёт. Захотел создать киборга, который только работает и иногда подзаряжается — без проблем. Сделал ему Workable и Chargeable. Не надо городить огород и ломать существующий код.
  • Поддержка упрощается. Когда меняешь интерфейс Eatable (добавил, скажем, метод digest()), тебе не нужно бегать и править класс Robot. Он от этой ебалы вообще не зависит. Удивление пиздец, как же это удобно!
  • Пустых реализаций не будет. Больше не увидишь в коде этих позорных override fun eat() {} или throw NotImplementedException(). Код становится честным.

Если игнорировать этот принцип, получится монстр, который знает всё, но ничего толком не умеет. Это как если бы твой телевизор имел интерфейс «Устройство для готовки», и чтобы включить его, тебе пришлось бы реализовывать метод fryEggs(). Полный абсурд, да?

Так что запомни: много маленьких и точных интерфейсов всегда лучше, чем один большой и всеобъемлющий, который пытается угодить всем, но в итоге всех только ебёт.