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

4.09. Dependency Inversion

Разработчику Архитектору Инженеру

Принцип D: Dependency Inversion

Это пятый принцип SOLID. Его часто путают с DI, но это разные вещи.

Dependency Inversion Principle (DIP) гласит:

  1. Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций.
  2. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

Представим, что у нас есть лампочка (LightBulb), которую можно включить и выключить. И у нас есть переключатель (Switch), который может управлять лампочкой. Если мы будем создавать класс Switch, то он должен установить связь с LightBulb.

class LightBulb {
public void turnOn() { ... }
}

class Switch {
private LightBulb bulb = new LightBulb(); // жёсткая зависимость
}

Но при таком подходе (это кстати обычная связь без DIP), Switch зависит от конкретной реализации Lightbulb, и нельзя подключить другие девайсы, пока прямо их не перечислим. И если применить DIP, устанавливая задачу, чтобы Switch не зависел от конкретного вида устройство, а был более универсальным:

interface Switchable {
void turnOn();
void turnOff();
}

class LightBulb implements Switchable { ... }
class Fan implements Switchable { ... }

class Switch {
private Switchable device; // зависит от абстракции
public Switch(Switchable device) {
this.device = device;
}
}

Здесь можно увидеть, что добавляется интерфейс Switchable (переключаемый). Класс Lighbulb наследует Switchable, и Fan тоже Switchable - словом, как лампочка, так и вентилятор - оба «переключаемые» и теоретически, к ним можно применять класс. А класс Swich изменил подход, создав себе device, у которого тип данных - Switchable.

Класс Switch (переключатель) получает поле device, которое потом используется в методе Switch, что сделало его универсальным - класс больше не зависит от конкретики, и стал более гибким, расширяемым и тестируем.

Смысл? А теперь нам не нужно будет создавать Fan и прочие устройства со своими экземплярами в Switch. Теперь мы просто при вызове конструктора Switch(Switchable device) будем передавать любой из Switchable.