Уточнение различий.
И принцип инверсии зависимостей, и внедрение зависимостей - совершенно разные вещи, несмотря на схожесть названий терминов. Понимание различий важно для инженеров-программистов, занимающихся объектно-ориентированным программированием.
Только очень простые или несколько низкоуровневых объектов могут независимо реализовать всю необходимую им функциональность. Обычно объекты должны повторно использовать логику других объектов. Для этого объект может просто создать экземпляры всех необходимых зависимостей самостоятельно, используя ключевое слово new
:
public class OrderService { private OrderRepository _orderRepository = new OrderRepository(); public Order PrepareOrder(long orderId) { var order = _orderRepository.GetOrder(orderId); //... } }
Объект OrderService
может повторно использовать логику объекта OrderRepository
. Вроде сейчас все хорошо. Каждый компонент отвечает за свою работу, компоненты взаимодействуют друг с другом.
Однако использование ключевого слова new
создает тесную связь между объектом и его зависимостями. Тесная связь делает класс OrderService
непригодным для повторного использования кода в новом контексте, где репозиторий не нужен. Также нельзя писать модульные тесты для класса OrderService
. Отсутствие внедрения зависимостей может серьезно повлиять на ремонтопригодность приложения. Вот почему внедрение зависимостей - распространенная передовая практика в разработке программного обеспечения.
Внедрение зависимостей - это метод, который позволяет объекту получать другие объекты, от которых он зависит. Наиболее распространенный подход - внедрить интерфейс зависимости в конструктор класса следующим образом:
public class OrderService { private IOrderRepository _orderRepository; public OrderService(IOrderRepository orderRepository) { _orderRepository = orderRepository; } public Order PrepareOrder(long orderId) { var order = _orderRepository.GetOrder(orderId); //... } }
Помимо внедрения конструктора, есть еще два метода: внедрение метода и внедрение свойства. Внедрение зависимости метода может использоваться, когда некоторая зависимость необходима для одного метода класса. Внедрение зависимостей свойств обычно используется для установки зависимостей, которые могут изменяться несколько раз в течение жизненного цикла класса.
Использование внедрения конструктора в сочетании с интерфейсом помогло нам инвертировать OrderRepository
зависимость - класс OrderService
больше не мог создавать экземпляры OrderRepository
. Зависимость должна быть создана кем-то другим и передана в конструктор OrderService
.
В этом конкретном примере внедрение интерфейса в конструктор заставило код следовать принципу инверсии зависимостей. Но ключевой момент всей истории в следующем:
Не каждый объект, которому требуется интерфейс в конструкторе, следует принципу инверсии зависимостей.
Принцип инверсии зависимостей - это гораздо больше, чем просто внедрение интерфейса в конструктор. Принцип гласит, что объект должен зависеть от объектов на том же или более высоком уровне абстракции.
public class OrderService { private ISqlConnection _sqlConnection; public OrderService(ISqlConnection sqlConnection) { _sqlConnection = sqlConnection; } }
В этом примере класс OrderService
по-прежнему получает интерфейс в конструкторе, но, несмотря на это, OrderService
нарушает принцип инверсии зависимостей. Класс ISqlConnection
существует на несколько уровней ниже класса OrderService
, который должен иметь дело только с репозиториями (которые на один уровень ниже) или другими объектами домена (которые находятся на том же уровне). Работа с соединениями, сокетами или другими низкоуровневыми деталями является нарушением инверсии зависимостей, что может значительно усложнить реализацию класса OrderService
.
Принцип инверсии зависимостей помогает разработчикам организовать компоненты приложения (классы и модули) в четкую иерархию, в то время как внедрение зависимостей - это простой метод, позволяющий избежать тесной связи между классами.
Резюме
- Внедрение зависимостей - это метод, который позволяет объекту получать другие объекты, от которых он зависит, через конструктор, метод или свойство.
- Внедрение зависимости ничего не говорит о том, к каким уровням абстракции должна принадлежать зависимость.
- Принцип инверсии зависимостей гласит, что зависимость должна существовать на том же или более высоком уровне абстракции.
- Принцип инверсии зависимостей реализован с помощью техники внедрения зависимостей.