В чем разница между инверсией контроля и инверсией зависимостей
Что проверяют
Различаете ли вы три близких термина: инверсию управления (IoC), принцип инверсии зависимостей (DIP, буква D из SOLID) и внедрение зависимостей (DI). Их постоянно смешивают, а интервьюеру важно, чтобы вы видели: IoC про поток управления, DIP про направление зависимостей в коде. Следом часто просят показать это на Spring.
Как отвечать
- IoC: не ваш код вызывает библиотеку, а фреймворк вызывает ваш код. Вы не пишете цикл обработки запросов, а объявляете
@RestController, и Spring сам решает, когда его создать и вызвать. Это «принцип Голливуда»: не звоните нам, мы позвоним вам. Сюда же относятся колбэки, шаблонный метод, слушатели событий; - DIP: модули верхнего уровня не зависят от модулей нижнего, оба зависят от абстракции.
OrderServiceзнает интерфейсPaymentGateway, а неStripeClient. Интерфейс принадлежит слою бизнес-логики, поэтому зависимость в исходниках «переворачивается»: инфраструктура зависит от домена, а не наоборот; - DI: объект получает зависимости снаружи (через конструктор, сеттер или поле), а не создаёт их через
new. Это частный случай IoC, применённый к созданию объектов.
Связь такая: DIP говорит, от чего зависеть, DI доставляет конкретную реализацию, а IoC-контейнер (ApplicationContext в Spring) делает это за вас. DIP можно соблюдать без всякого контейнера, передавая реализацию в конструктор руками. И наоборот, можно работать со Spring и нарушать DIP, внедряя конкретный класс вместо интерфейса.
Про Spring стоит добавить, что предпочтительное внедрение через конструктор: поля становятся final, зависимости видны явно, а тест собирает объект без контейнера. При единственном конструкторе @Autowired не нужен.
Где спотыкаются
Называют инверсию зависимостей сокращением DI и описывают внедрение через конструктор. Это подмена: принцип и техника разные вещи.
Сводят IoC к Spring. Контейнер лишь одна из реализаций, а сама идея старше и шире: любой фреймворк с колбэками уже инвертирует управление.
Пугают потерей производительности. Внедрение происходит при старте контекста, вызов через интерфейс в рабочем пути почти бесплатен, а реальная цена в скорости запуска и сложности конфигурации.