4.09. Dependency Inversion
Принцип D: Dependency Inversion
Это пятый принцип SOLID. Его часто путают с DI, но это разные вещи.
Dependency Inversion Principle (DIP) гласит:
- Модули верхнего уровня не должны зависеть от модулей нижнего уровня. Оба должны зависеть от абстракций.
- Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
Представим, что у нас есть лампочка (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.