IoC.Resolve vs Конструктор Инъекция
Я слышал, как многие люди говорили, что использовать IoC.Resolve() - это плохая практика, но я никогда не слышал веских причин, почему (если все дело в тестировании, вы можете просто посмеяться над контейнером, и все готово).
теперь преимущества использования Resolve вместо Constructor Injection заключаются в том, что вам не нужно создавать классы, которые имеют 5 параметров в конструкторе, и всякий раз, когда вы собираетесь создавать экземпляр этого класса, вам не нужно предоставлять его что-нибудь
8 ответов
IoC.Resolve<>
пример шаблона локатора службы Этот шаблон налагает несколько ограничений, которых нет у конструктора:
- Из-за статических вызовов объекты могут иметь не более детализированный контекст, чем домен приложения.
- Объекты решают, какие версии зависимостей разрешать. Все экземпляры определенного класса получат одинаковую конфигурацию зависимостей.
- Искушение связать код с контейнером велико, например, вместо создания фабрики, раскрывающей намерения.
- Модульное тестирование требует конфигурации контейнера, где классы могут быть просто созданы и использованы в противном случае. (Это особенно хлопотно, когда вы хотите протестировать несколько конфигураций одного класса из-за второй проблемы, описанной выше.)
- Структура приложения не может быть выведена из его открытого API. (Параметры конструктора - это хорошо. Они не являются проблемой, которую вам нужно решить.)
Эти ограничения, на мой взгляд, сводят шаблон ServiceLocator к среднему положению между "большим шариком грязи" и внедрением зависимостей: полезно, если вам нужно его использовать, но далеко не лучший выбор.
Если вы создаете классы с 5 зависимостями, у вас есть проблемы, отличные от IoC.Resolve.
Извлечение зависимостей (в отличие от их проталкивания через конструктор) полностью пропускает смысл использования инфраструктуры IoC. Вы хотите инвертировать зависимости. Не ваши классы зависят от инфраструктуры IoC, а наоборот.
Если вам не нужны все зависимости в определенных сценариях, то, возможно, вам следует разделить свой класс или сделать некоторые зависимости необязательными, сделав их зависимостями свойств.
Ваши занятия зависят от контейнера. Они не будут работать, если вы не предоставите им один. Будь то настоящий или фальшивый, не имеет значения. Они по своей природе связаны с контейнером через статическую зависимость. Это налагает на вас дополнительную работу, чтобы делать что-либо с вашими классами. Каждый раз, когда вы хотите использовать свой класс, вам нужно перетащить контейнер с ними. Без пользы! Сервисный локатор - это всего лишь один глобальный пакет всего, что противоречит, вероятно, всем принципам объектно-ориентированного программирования.
Ioc.Resolve по сути является шаблоном Service Locator. Это имеет свое место, но не идеально. Внедрение в конструктор предпочтительнее с точки зрения архитектуры, поскольку зависимости более явные, тогда как SL скрывает зависимости внутри класса. Это снижает тестируемость и делает процесс более сложным, чем необходимо.
Если позволите, я бы посоветовал вам прочитать мою недавнюю серию о методах уменьшения связывания кода, которая охватывает SL, DI и IoC.
Одним из преимуществ является то, что при внедрении в конструктор все зависимости класса видны заранее.
С .Resolve
Вы должны прочитать код, чтобы выяснить зависимости.
Я должен отметить, что не обязательно пропускать инъекцию конструктора и использовать статическую инъекцию. Для этого есть отличные приложения, самый конкретный пример - использование его в реализации шаблона Factory.
public static class ValidationFactory
{
public static Result Validate<T>(T obj)
{
try
{
var validator = ObjectFactory.GetInstance<IValidator<T>>();
return validator.Validate(obj);
}
catch (Exception ex)
{
var result = ex.ToResult();
...
return result;
}
}
}
Я использую это с StructureMap для обработки моего слоя проверки.
Редактировать: Другой пример использования контейнера непосредственно у меня - сделать некоторые из ваших доменных объектов одиночными, не превращая их в статические классы и не вводя всю странность, которую делают статические классы.
В некоторых моих взглядах я связываю некоторые сущности, подобные этой. Обычно я бы использовал Enum с атрибутом Description, чтобы дать мне 3 варианта значения, но 3-й в этом случае также должен быть строкой, а не int, поэтому я создал интерфейс с этими 3 свойствами и унаследовал все объекты домена от Это. Затем мой контейнер сканирует мою сборку и автоматически регистрирует их, а затем вынимает их.
SomeObject ISomeView.GetMyObject
{
get { return new SomeObject { EmpoweredEnumType =
ObjectFactory.GetNamedInstance<IEmpEnum>("TheObjectName");
}
}
Поскольку вопрос спорный, я не собираюсь говорить "используй то или это"
Похоже, что использование Service Locator не является плохой вещью, если вы можете положиться на него (и мы обычно так или иначе делаем на некоторой платформе DI). С DI мы можем легко изменить каркас, с помощью Service Locator мы создаем связь с SL частью каркаса.
Что касается Брайана Уоттса, ответьте, когда будете читать дальше в Service Locator vs Dependency Injection
... При внедрении [constructor] явный запрос отсутствует, служба появляется в классе приложения - отсюда и инверсия управления.
Инверсия контроля - это общая черта фреймворков, но это то, что имеет свою цену. Как правило, это трудно понять и приводит к проблемам при попытке отладки. Так что в целом я предпочитаю избегать этого, если мне это не нужно. Это не значит, что это плохо, просто я думаю, что нужно оправдать себя более простой альтернативой.
И затем, если вы прочитаете позже, это еще одно оправдание для фактического использования инжектора конструктора (инверсия управления).
Мое мнение таково, что в небольших проектах нормально использовать SL, так как главное не создавать связь между нашими специально разработанными классами.
Используя StructureMap exmaple, это должно быть приемлемо:
public class Demo
{
private ISomething something = ObjectFactory.GetInstance<ISomething>();
private IFoo foo = ObjectFactory.GetInstance<IFoo>();
}
Да, код зависит от SM Frx, но как часто вы меняете DI Frx?
А для модульного тестирования можно настроить макет
public class SomeTestClass
{
public SomeTest()
{
ObjectFactory.Inject<ISomething>(SomeMockGoesHere);
ObjectFactory.Inject<IFoo>(SomeMockGoesHere);
Demo demo = new Demo() //will use mocks now
}
}
Преимущества использования Resolve вместо Constructor Injection в том, что вам не нужно создавать классы с 5 параметрами в конструкторе
но вы можете сделать больше "сантехники" для модульного тестирования.
вам не нужно создавать классы, которые имеют 5 параметров в конструкторе, и всякий раз, когда вы собираетесь создать экземпляр этого класса, вам не нужно предоставлять ему что-либо
Пара баллов:
- Если вы используете DI-контейнер, он должен создавать для вас экземпляры этого класса. В этом случае вам не нужно предоставлять что-либо самостоятельно для производственного использования. Для тестирования вы должны будете предоставить ему зависимости через конструктор, но:
- Если класс зависит (использует каким-то образом) те 5 вещей, о которых вы говорите, предоставляя конструктору (и вы бы не предоставили их, если бы этого не было), вам БУДЕТ предоставить его им одним или другим способом. Другой. При тестировании (это единственный раз, когда вам нужно вызывать конструктор самостоятельно), вы можете либо передать ему эти вещи через конструктор, либо написать код для настройки контейнера и добавить эти 5 вещей в него, чтобы при
IoC.Resolve()
называется, они на самом деле там. Передать их конструктору намного проще, я бы сказал.
Зависимости будут существовать, даже если вы не сделаете это очевидным через API класса (в данном случае это конструктор). Однако, будет намного сложнее понять и протестировать классы, которые пытаются скрыть свои зависимости, как это.
Я бы сказал, что это безумное количество параметров, которые нужно ввести.
Стремитесь к одному параметру, может быть, максимум 2, что возможно почти во всех сценариях, и, конечно, должен быть интерфейс. Более того, и я чувствую запах крысы (недостаток дизайна).