IoC.Resolve vs Constructor Injection

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

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

Ответ 1

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

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

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

Ответ 2

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

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

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


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

Ответ 3

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

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

Ответ 4

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

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

Ответ 5

Я должен указать, что это не обязательно зло, чтобы пропустить инъекцию конструктора и использовать статическую инъекцию. Есть большие приложения этого, наиболее конкретный пример - использование его в реализации шаблона 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 варианта значений, но третий в этом случае должен быть также строкой, а не int, поэтому я создал интерфейс с этими 3 свойствами и наследовал все объекты домена из Это. Затем у меня есть контейнер, который сканирует мою сборку и автоматически регистрирует их все, чтобы вытащить их. Я просто

SomeObject ISomeView.GetMyObject
{
    get { return new SomeObject { EmpoweredEnumType = 
            ObjectFactory.GetNamedInstance<IEmpEnum>("TheObjectName");
        }
}

Ответ 6

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

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

Что касается Брайана Уотта, ответьте, когда прочтете позже "Локатор сервисов" и "Инъекция зависимостей"

... При инъекции [constructor] нет явного запроса, служба появляется в классе приложения - следовательно, инверсия элемента управления.

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

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

Мое мнение таково, что в небольших проектах это нормально использовать 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 параметрами в конструкторе

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

Ответ 7

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

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

Ответ 8

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

Пара точек:

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

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