Пятым и последним принципом в списке принципов SOLID является принцип инверсии зависимости (Dependency Inversion Principle; DIP), который Роберт Мартин в книге «Чистая архитектура» формулирует следующим образом: «Код, реализующий высокоуровневую политику, не должен зависеть от кода, реализующего низкоуровневые детали. Напротив, детали должны зависеть от политики«.
Оригинальная формулировка принципа состоит из двух частей:
- Высокоуровневые модули не должны ничего импортировать из низкоуровневых модулей. И те и другие должны зависеть от абстракций (например, интерфейсов)
- Абстракции не должны зависеть от деталей. Детали (конкретные реализации) должны зависеть от абстракций.
Принцип инверсии зависимости говорит о том, что при разработке программного обеспечения важно опираться на абстракции, а не на конкретные реализации, так как любое изменение в интерфейсе или абстрактном классе будет приводить к изменениям в конечных реализациях, но не каждое изменение в конечной реализации потребует изменений в интерфейсе или абстрактном классе. И именно в том, что компоненты высокоуровневых и низкоуровневых модулей должны зависеть от абстракций, а не друг от друга, и заключается инверсия зависимости.
Уровни компонентов
Самые низкоуровневые компоненты в случае с приложениями на платформе Java — это различные клиентские библиотеки, которые помогают нам взаимодействовать с какими-то сторонними сервисами и приложениями: базами данных, очередями сообщений, серверами кэша и т.д.
Чуть выше уровнем стоят фреймворки, предоставляющие типовые решения для тех или иных задач. К ним можно отнести Spring Framework и Hibernate.
Выше идут компоненты нашего приложения, позволяющие взаимодействовать с этими фреймворками и библиотеками, таковыми в данном случае являются TaskController
и TaskRepository
.
На самом верху данного примера находится TaskService
, так как он реализует самые высокоуровневую логику — бизнес-логику, характерную для деятельности предприятия, для которого разработано приложение.
В действительности уровней может быть ещё больше. Если вы добавите в ваш проект практики, характерные для предметно-ориентированного проектирования (Domain-Driven Design; DDD), то выше уровнями добавятся сервисы бизнес-логики, классы-сущности и агрегаты.
Применение принципа
Если мы представим себе «классическое» в общих чертах клиент-серверное приложение без соблюдения принципа инверсии зависимости, то картина будет следующая:

Как видим, в данном случае все компоненты зависят друг от друга, то есть от конкретных классов, а не от абстракций. Это первое нарушение принципа инверсии зависимости. Второе нарушение заключается в зависимости TaskService
от TaskRepository
, так как TaskService
— более высокоуровневый компонент, чем TaskRepository
.
В коде описанная схема будет выглядеть следующим образом:
Самая главная проблема этого кода — высокая связанность между компонентами, из-за чего код получается негибким, и, например, не получится добавить кэширование между TaskService
и TaskRepository
без внесения изменений в уже существующий код при помощи шаблона проектирования «декоратор».
Применение принципа инверсии зависимости к иерархии классов, продемонстрированной выше, изменит её следующим образом:

После применения принципа инверсии зависимости между компонентами нет прямых связей, так как теперь они зависят от абстракций, а код должен выглядеть следующим образом:
В результате связанность между компонентами снижается, а код проекта становится более гибким, позволяя применять шаблоны проектирования для расширения поведения без внесения изменений в существующий код.
Полезные ссылки
- Статья в VK
- Поддержать автора: стать доном или подкинуть звонкую монету в Boosty или Tinkoff