IoC.Resolve vs Конструктор Инъекция - PullRequest
43 голосов
/ 01 февраля 2010

Я слышал, что многие люди говорят, что использовать IoC.Resolve () - это плохая практика, но я никогда не слышал веских причин, почему (если все дело в тестировании, вы можете просто высмеять контейнер, и вы сделано).

теперь преимущества использования Resolve вместо Constructor Injection в том, что вам не нужно создавать классы, которые имеют 5 параметров в конструкторе, и всякий раз, когда вы собираетесь создать экземпляр тот класс, который вам не нужен, чтобы предоставить ему что-нибудь

Ответы [ 8 ]

49 голосов
/ 01 февраля 2010

IoC.Resolve<> является примером шаблона Service Locator . Этот шаблон налагает несколько ограничений, которых нет в конструкторе:

  • Объекты могут иметь не более детальный контекст, чем домен приложения, из-за статических вызовов
  • Объекты решают , какие версии зависимостей разрешать. Все экземпляры определенного класса получат одинаковую конфигурацию зависимостей.
  • Искушение связать код с контейнером велико, например, вместо создания фабрики, раскрывающей намерения.
  • Модульное тестирование требует настройки контейнера, где классы могут быть просто созданы и использованы в противном случае. (Это особенно хлопотно, когда вы хотите протестировать несколько конфигураций одного класса из-за второй проблемы, описанной выше.)
  • Структура приложения не может быть выведена из его открытого API. (Параметры конструктора - это хорошо . Они не являются проблемой, которую вам необходимо решить.)

Эти ограничения, на мой взгляд, переводят шаблон Service Locator в середину между «большим грязевым шариком» и внедрением зависимости: полезно, если вы должны его использовать, но далеко не лучший выбор.

26 голосов
/ 01 февраля 2010

Если вы создаете классы с 5 зависимостями, у вас есть проблемы, отличные от IoC.Resolve.

Вытягивание зависимостей (в отличие от их проталкивания через конструктор) полностью пропускает смысл использования инфраструктуры IoC. Вы хотите инвертировать зависимости. Не ваши классы зависят от инфраструктуры IoC, а наоборот.

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


Ваши занятия зависят от контейнера. Они не будут работать, если вы не предоставите им один. Будь то настоящий или фальшивый, не имеет значения. Они по своей сути связаны с контейнером через статическую зависимость. Это налагает на вас дополнительную работу, чтобы делать что-либо с вашими классами. Каждый раз, когда вы хотите использовать свой класс, вам нужно перетащить контейнер с ними. Без пользы! Сервисный локатор - это всего лишь один глобальный пакет всего, что противоречит, вероятно, всем принципам объектно-ориентированного программирования.

7 голосов
/ 02 февраля 2010

Ioc.Resolve по сути является шаблоном Service Locator. Это имеет свое место, но не идеально. Внедрение в конструктор предпочтительнее с точки зрения архитектуры, поскольку зависимости более явные, тогда как SL скрывает зависимости внутри класса. Это снижает тестируемость и делает процесс более сложным, чем необходимо.

Если позволите, я бы посоветовал вам прочитать мою серию , посвященную методам уменьшения связывания кода, которые охватывают SL, DI и IoC.

3 голосов
/ 02 февраля 2010

Одним из преимуществ является то, что при внедрении в конструктор все зависимости класса видны заранее.

С .Resolve вы должны прочитать код, чтобы выяснить зависимости.

1 голос
/ 28 июля 2015

Поскольку вопрос спорный, я не собираюсь говорить "используйте то или другое"

Похоже, что использование Service Locator не является плохой вещью, если вы можете положиться на него (и мы обычно так или иначе делаем на некоторой платформе DI). С помощью DI мы можем легко изменить структуру, а с помощью Service Locator мы создаем связь с частью SL платформы.

Что касается ответа Брайана Уоттса, когда вы будете читать дальше в Сервисный локатор против внедрения зависимостей

... При внедрении [constructor] явный запрос отсутствует, служба появляется в классе приложения - отсюда инверсия управления .

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

И затем, если вы прочтете позже, это еще одно оправдание для фактического использования инжектора конструктора (инверсия управления).

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

При использовании структуры StructureMap это должно быть приемлемо:

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 параметрами в конструкторе

, но вы можете сделать больше «сантехники» для модульного тестирования.

1 голос
/ 01 февраля 2010

Я должен отметить, что не обязательно пропускать инъекцию конструктора и использовать статическую инъекцию. Для этого есть отличные приложения, самый конкретный пример - использование его в реализации шаблона 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");
        }
}
0 голосов
/ 01 февраля 2010

вам не нужно создавать классы, которые имеют 5 параметров в конструкторе, и всякий раз, когда вы собираетесь создавать экземпляр этого класса, вам не нужно предоставлять ему что-либо

Пара баллов:

  • Если вы используете DI-контейнер, он должен создавать для вас экземпляры этого класса. В этом случае вам не нужно предоставлять что-либо самостоятельно для производственного использования. Для тестирования вы должны будете предоставить ему зависимости через конструктор, но:
  • Если класс зависит (использует каким-то образом) те 5 вещей, о которых вы говорите, предоставляя конструктору (и вы не предоставили бы их, если бы этого не было), вам БУДЕТ предоставить ему одно так или иначе. При тестировании (это единственный раз, когда вам нужно вызывать конструктор самостоятельно), вы можете либо передать ему эти вещи через конструктор, либо написать код для настройки контейнера и добавить эти 5 вещей в него, чтобы при IoC.Resolve() называется, они на самом деле там. Я бы сказал, что передать их конструктору намного проще.

Зависимости будут существовать, даже если вы не сделаете это очевидным через API класса (в данном случае это конструктор). Тем не менее, будет намного сложнее понять и протестировать классы, которые пытаются скрыть свои зависимости следующим образом.

0 голосов
/ 01 февраля 2010

Я бы сказал, что это безумное количество параметров для ввода.

Стремитесь к одному параметру, может быть максимум 2, что возможно почти в всех сценариях и, конечно, должен быть интерфейсом. Более того, и я чувствую запах крысы (недостаток дизайна).

...