Понимание принципа инверсии зависимостей
Низкоуровневые модули теперь зависят от абстракций, а не наоборот
Принцип инверсии зависимостей (Dependency Inversion Principle, DIP) говорит, что:
- Высокоуровневые модули (логика) не должны зависеть от низкоуровневых (деталей реализации). Оба должны зависеть от абстракций.
- Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
Плохой код без инверсии зависимостей
Допустим, у нас есть сервис, который работает напрямую с базой данных:
Проблемы:
- Сервис жёстко привязан к MySQL.
- Если нужно заменить MySQL на PostgreSQL, придётся менять код.
- Код сложно тестировать, потому что нельзя подставить фейковую реализацию.
Правильный код с инверсией зависимостей
Сначала создаём абстракцию (интерфейс):
Теперь сервис будет зависеть не от конкретной базы данных, а от интерфейса:
Теперь реализуем этот интерфейс для MySQL:
И для PostgreSQL:
Теперь мы можем легко менять реализацию без изменения кода сервиса:
Раньше высокоуровневый модуль (UserService) зависел от деталей (MySQL). Теперь детали (MySQL, PostgreSQL) зависят от абстракции (UserRepository).
Это и есть инверсия зависимостей: теперь низкоуровневые модули должны реализовать интерфейс, который диктует высокоуровневый модуль.
Когда мы говорим «API (или БД или любой другой низкоуровневый модуль) зависит от интерфейса», мы имеем в виду, что он реализует этот интерфейс.
То есть, вместо того чтобы логика программы жёстко зависела от конкретного API-клиента или базы данных, мы сначала создаём абстракцию (интерфейс), а потом реализация API или БД «подстраивается» (зависит) под него.
Пример: как API зависит от интерфейса
Допустим, у нас есть сервис, который получает данные либо из API, либо из БД.
Создаём интерфейс (DataSource
)
Теперь не сервис зависит от API или БД, а API и БД зависят от интерфейса:
API-клиент реализует этот интерфейс
Теперь API зависит от интерфейса, потому что оно реализует DataSource
:
БД тоже реализует интерфейс
Точно так же БД теперь зависит от интерфейса:
Сервис работает через интерфейс
Теперь наш сервис вообще не знает, API это или БД — он работает только с DataSource
:
Запускаем код
Теперь можно легко переключаться между API и БД:
Вывод
API и БД теперь «зависят» от интерфейса, потому что они обязаны его реализовать.
Сервис теперь не знает деталей реализации, а работает через интерфейс.
Мы можем легко подставлять другие реализации без изменения кода DataService
.
То есть «зависимость» в этом контексте — это не «использование», а «подчинение интерфейсу».
Recommended Posts
health-check API для микросервисов
10.03.2024
Отказоустойчивый кластер Postgresql
02.02.2024