Что такое принцип инверсии зависимостей (DIP) и как он связан с внедрением зависимостей?
Принципы S.O.L.I.D
Принцип инверсии зависимостей: модули верхнего и нижнего уровня зависят от абстракций; внедрение зависимостей — техника, которая передаёт реализацию извне вместо её создания внутри класса.
Без DIP
class OrderService {
constructor() {
this.repo = new PostgresOrderRepository(); // жёсткая связь
}
}Сервис приклеен к конкретной базе: ни заменить, ни протестировать без инфраструктуры.
С DIP
Сервис объявляет зависимость от интерфейса OrderRepository; абстракция принадлежит верхнему уровню (её называют портом), а PostgresOrderRepository — адаптер нижнего уровня, подставляемый через конструктор. Направление зависимости в коде инвертировано относительно потока управления.
DI как следствие
- Конструкторское внедрение — стандарт; DI-контейнеры лишь автоматизируют сборку графа объектов.
- Бонус: в тестах подставляется in-memory реализация.
- Service Locator — антипод: прячет зависимость внутри класса, а внедрение делает её видимой в сигнатуре.
Границы
Интерфейс на каждый класс «про запас» — шум: абстракцию вводят там, где реально нужны вариативность или изоляция в тестах.
Коротко для собеседования
DIP разворачивает зависимость: бизнес-логика владеет абстракцией, а инфраструктура её реализует. DI — практический способ этого добиться: реализацию передают через конструктор, и в тестах легко подставить in-memory версию; заводить интерфейс на каждый класс без нужды не стоит.