Я прошу прощения, если это кажется еще одним повторением вопроса, но каждый раз, когда я нахожу статью, касающуюся этой темы, в основном это просто говорит о том, что DI. Итак, я получаю DI, но я пытаюсь понять потребность в контейнере IoC, в который, кажется, все входят. Действительно ли смысл контейнера IoC просто «автоматически разрешать» конкретную реализацию зависимостей? Может быть, мои классы, как правило, не имеют нескольких зависимостей, и, возможно, именно поэтому я не вижу ничего сложного, но я хочу убедиться, что я правильно понимаю полезность контейнера.
Обычно я разбиваю свою бизнес-логику на класс, который может выглядеть примерно так:
public class SomeBusinessOperation
{
private readonly IDataRepository _repository;
public SomeBusinessOperation(IDataRespository repository = null)
{
_repository = repository ?? new ConcreteRepository();
}
public SomeType Run(SomeRequestType request)
{
// do work...
var results = _repository.GetThings(request);
return results;
}
}
Таким образом, он имеет только одну зависимость, а в некоторых случаях он может иметь вторую или третью, но не так часто. Поэтому все, что вызывает это, может передать свое собственное репо или позволить ему использовать репо по умолчанию.
Что касается моего текущего понимания контейнера IoC, то все, что он делает - это разрешает IDataRepository. Но если это все, что он делает, то я не вижу в этом тонны ценности, поскольку мои операционные классы уже определяют запасной вариант, когда никакая зависимость не передается. Поэтому единственное другое преимущество, о котором я могу думать, это то, что если у меня есть несколько таких операций, как это использует тот же резервный репо, я могу изменить это репо в одном месте, которое является реестром / фабрикой / контейнером. И это здорово, но так ли это?
источник
ConcreteRepository
и (2) вы можете предоставить дополнительные зависимостиConcreteRepository
(например, подключение к базе данных будет обычным).Ответы:
Контейнер IoC не относится к случаю, когда у вас есть одна зависимость. Речь идет о случае, когда у вас есть 3 зависимости, и у них есть несколько зависимостей, которые имеют зависимости и т.д.
Это также поможет вам централизовать разрешение зависимостей и управление жизненными циклами зависимостей.
источник
Существует ряд причин, по которым вы можете использовать контейнер IoC.
Unreferenced dll
Вы можете использовать контейнер IoC для разрешения конкретного класса из dll, на который нет ссылок. Это означает, что вы можете полностью получить зависимости от абстракции, то есть от интерфейса.
Избегайте использования
new
Контейнер IoC означает, что вы можете полностью исключить использование
new
ключевого слова для создания класса. Это имеет два эффекта. Во-первых, это разъединяет ваши классы. Второе (что связано) заключается в том, что вы можете добавить макеты для модульного тестирования. Это невероятно полезно, особенно когда вы взаимодействуете с длительным процессом.Написать против абстракций
Использование контейнера IoC для разрешения ваших конкретных зависимостей позволяет вам писать свой код против абстракций, а не реализовывать каждый конкретный класс, который вам нужен, когда вам это нужно. Например, вам может понадобиться ваш код для чтения данных из базы данных. Вместо того, чтобы писать класс взаимодействия с базой данных, вы просто пишете интерфейс для него и код против этого. Вы можете использовать макет для проверки функциональности кода, который вы разрабатываете, по мере его разработки, вместо того, чтобы полагаться на разработку конкретного класса взаимодействия с базой данных, прежде чем вы сможете протестировать другой код.
Избегайте хрупкого кода
Другая причина использования контейнера IoC заключается в том, что, полагаясь на контейнер IoC для разрешения ваших зависимостей, вы избегаете необходимости изменять каждый отдельный вызов конструктора класса при добавлении или удалении зависимости. Контейнер IoC автоматически разрешит ваши зависимости. Это не большая проблема, когда вы создаете класс один раз, но это гигантская проблема, когда вы создаете класс в сотне мест.
Управление жизненным циклом и неуправляемая очистка ресурсов
Последняя причина, которую я упомяну, - это управление временем жизни объекта. Контейнеры IoC часто предоставляют возможность указать время жизни объекта. Имеет смысл указывать время жизни объекта в контейнере IoC, а не пытаться вручную управлять им в коде. Ручное управление временем жизни может быть очень сложным. Это может быть полезно при работе с объектами, которые требуют утилизации. Вместо того, чтобы вручную управлять удалением ваших объектов, некоторые контейнеры IoC будут управлять удалением для вас, что может помочь предотвратить утечки памяти и упростить вашу кодовую базу.
Проблема с примером кода, который вы предоставили, состоит в том, что класс, который вы пишете, имеет конкретную зависимость от класса ConcreteRepository. Контейнер IoC удалит эту зависимость.
источник
Согласно принципу единой ответственности каждый класс должен иметь только одну ответственность. Создание новых экземпляров классов является еще одной обязанностью, поэтому вы должны инкапсулировать этот вид кода в один или несколько классов. Вы можете сделать это с помощью любых шаблонов создания, например, фабрик, сборщиков, DI-контейнеров и т. Д.
Есть и другие принципы, такие как инверсия управления и инверсия зависимостей. В этом контексте они связаны с созданием зависимостей. Они утверждают, что классы высокого уровня должны быть отделены от классов низкого уровня (зависимостей), которые они используют. Мы можем отделить вещи, создавая интерфейсы. Таким образом, классы низкого уровня должны реализовывать определенные интерфейсы, а классы высокого уровня должны использовать экземпляры классов, которые реализуют эти интерфейсы. (примечание: единообразное ограничение интерфейса REST применяет тот же подход на системном уровне.)
Сочетание этих принципов на примере (извините за некачественный код, я использовал какой-то специальный язык вместо C #, так как я этого не знаю):
Нет SRP, нет IoC
Ближе к SRP, нет IoC
Да SRP, нет IoC
Да SRP, да IoC
Таким образом, мы получили код, в котором вы можете заменить каждую конкретную реализацию другой, которая реализует тот же интерфейс c. Так что это хорошо, потому что участвующие классы отделены друг от друга, они знают только интерфейсы. Еще одно преимущество в том, что код создания экземпляров можно использовать повторно.
источник