Что такое Инверсия управления и Внедрение зависимостей (IoС & DI)

Паттерн (шаблон) проектирования — это продуманный способ построения исходного кода программы для решения часто возникающих в повседневном программировании проблем проектирования. Иными словами, это уже придуманное решения, для типичной задачи. При этом паттерн не готовое решение, а просто алгоритм действий, который должен привести к желаемому результату. Но сегодня мы рассмотрим не конкретный паттерн проектирования, а принцип построения объектно-ориентированных приложений Инверсия управления (IoC).

Инверсия управления (Inversion of Control, IoC) это определенный набор рекомендаций, позволяющих проектировать и реализовывать приложения используя слабое связывание отдельных компонентов. То есть, для того чтобы следовать принципам Инверсии управления нам необходимо:

  • Реализовывать компоненты, отвечающие за одну конкретную задачу;
  • Компоненты должны быть максимально независимыми друг от друга;
  • Компоненты не должны зависеть от конкретной реализации друг друга.

Одним из видов конкретной реализации данных рекомендаций является механизм Внедрения зависимостей (Dependency Injection, DI). Он определяет две основные рекомендации:

  • модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций;
  • абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

То есть, если у нас будут существовать два связанных класса, то нам необходимо реализовывать связь между ними не напрямую, а через интерфейс. Это позволит нам при необходимости динамически менять реализацию зависимых классов.

Предположим, что мы решили написать свою собственную программу, выполняющую крипто вычисления, другим словом майнер. Любая криптовалюта основана на какой-либо хэш-функции (алгоритме). Предположим, что наша программа будет выполнять вычисления на алгоритме SHA256, для майнинга биткоина. Тогда мы получим следующую связь между классами:

No DI

No DI

Проблема состоит в том, что в настоящее время алгоритмов, на которых основаны крипто валюты достаточно много и их число постоянно увеличивается. Если мы захотим добавить новые алгоритмы для майнинга других крипто валют, нам придется вносить большое количество изменений в сам класс майнера. Чтобы этого избежать, необходимо создать промежуточный интерфейс, от которого будет зависеть майнер и который должны будут реализовывать различные алгоритмы.

DI

DI

Так мы сможем не только разделить ответственность за выполнение конкретных задач между классом майнером и алгоритмами, но и сделаем задел на дальнейшее увеличение количества поддерживаемых алгоритмов.

Существует несколько конкретных паттернов Внедрения зависимостей:

  • Через конструктор (Constructor injection);
  • Через свойство класса (Setter injection);
  • Через аргумент метода (Method injection).

Давайте рассмотрим примеры реализации данных паттернов.

Для начала рассмотрим как можно реализовать данную задачу без Внедрения зависимостей. Мы создадим класс майнер, который будет запускать отдельный поток вычисления хеш-функции.

Реализация без инверсия управления

Так же мы создадим класс SHA256, который как раз и будет отвечать за нахождения хеша.

Теперь нам только и осталось создать экземпляр нашего майнера и запустить процесс майнинга.

В результате мы получим следующее:

Result no DI

Result no DI

Но как и упоминалось ранее, если мы захотим добавить новый алгоритм нам потребуется внести достаточно много изменений в код нашего класса майнера. Давайте внесем изменения в наши классы так, чтобы сделать их более гибкими и универсальными.

Внедрение зависимостей (DI)

Для начала добавим интерфейс для алгоритмов поиска хешей.

Создадим новый класс алгоритма Ethash и изменим существующий SHA256.

Внедрение зависимостей через конструктор

Изменим класс майнер следующим образом.

Как видите, теперь майнер не зависит от конкретного алгоритма, а принимает только интерфейс как аргумент конструктора.

Теперь нам немного нужно изменить вызов майнера в консольном приложении.

Внедрение зависимостей через свойство

Внедрение зависимостей через аргумент метода

Исходный код также можно посмотреть в репозитории https://github.com/shwanoff/DependencyInjection.

IoC-контейнер

Также хотелось бы упомянуть о IoC-контейнерах. IoC-контейнер (IoC-container) — это своеобразный фреймворк, позволяющий реализовывать Внедрение зависимостей более лаконичным способом, избавляя от рутины. Существует много различных IoC-контейнеров. Вот некоторые наиболее популярные из них:

В заключение хочется сказать, что IoC, DI — очень полезный механизм, но и как любой другой инструмент его нужно использовать только там, где он действительно необходим и принесет больше пользы, чем вреда. Ведь его реальное внедрение в небольшое консольное приложение, в котором вряд ли что-то будет меняться только усложнит архитектуру и понимание кода, а также увеличит вероятность совершения ошибки. В то же время если это серьезный крупный проект, где пожелания заказчика часто изменчивы и противоречивы, данный механизм может серьезно упростить жизнь разработчикам.

Рекомендую также изучить статью SOLID в объектно-ориентированном программировании. Инверсия управления входит в набор этих принципов, поэтому целесообразно изучить остальные.

P.S. Присоединяйся в любой удобной для тебя социальной сети. Для меня очень важно оставаться с тобой на связи, ведь у меня есть еще много полезной информации о программировании для тебя, которой я хочу с тобой поделиться.
[DISPLAY_ULTIMATE_PLUS]

%d такие блоггеры, как: