Введение
Программные системы состоят из модулей, которые мы можем условно поделить на низкоуровневые и высокоуровневые.
Низкоуровневые содержат утилитарную функциональность: обращение к БД, запросы к серверу, рендеринг DOM-элементов на странице.
Высокоуровневые содержат сложную, более абстрактную бизнес-логику. Они достаточно абстрактны, чтобы их можно было переиспользовать в разных проектах: авторизация пользователей, валидация форм, отправка уведомлений.
В устойчивых системах высокоуровневые модули, как правило, не требуют обновления при изменении низкоуровневых. Добиться подобной устойчивости помогает принцип инверсии зависимостей.
Принцип инверсии зависимостей
Принцип инверсии зависимостей (Dependency Inversion Principle, DIP) предполагает, что:
- Высокоуровневые модули не должны зависеть от низкоуровневых; оба типа должны зависеть от абстракций.
- Абстракции не должны зависеть от деталей, детали должны зависеть от абстракций.
Таким образом DIP помогает снизить зацепление модулей (coupling).
Когда модули жёстко зацеплены, они слишком много знают друг о друге и не функционируют по отдельности. В такой ситуации изменения в одном будут требовать изменений в других — что нарушает OCP.
Зацепление и связность
Зацепление (coupling) не стоит путать со связностью (cohesion).
Зацепление — степень взаимозависимости разных модулей. Чем выше зацепление, тем более хрупкой получается система, и тем сложнее вносить изменения.
Связность — степень, в которой задачи некоторого модуля, связаны друг с другом. Чем выше связность, тем строже модули следуют SRP, тем выше сфокусирован модуль на конкретной задаче.
Абстракции
Согласно принципу модули должны зависеть от других модулей не напрямую, а от абстракций.
В примере c регулятором температуры на схеме ниже структура системы нарушает DIP. Модули зависят напрямую от других модулей, это увеличивает зацепление.
Исправленный вариант вводит прослойку между сущностями в виде абстракций — интерфейсов.
Модулям теперь не обязательно работать с конкретными модулями, они могут работать с любой сущностью, которая реализует указанный интерфейс. Так снижается зацепление.
DIP и тестируемость
При тестировании модуля, который зависит от других модулей, нам нужно либо создавать экземпляр каждой зависимости, либо создать заглушки.
DIP упрощает тестирование системы. Если модули зависят от интерфейсов, нам достаточно создать заглушку, реализующую этот интерфейс.
Коротко
Принцип инверсии зависимостей:
- вводит правила и ограничения для зависимости одних модулей от других;
- снижает зацепление модулей;
- делает тестирование модулей проще;
- позволяет проектировать систему так, чтобы модули были заменяемы на другие.