Контейнер внедрения зависимостей в конструкторе

Почему поместить контейнер в конструктор так плохо? Например, вы хотите разрешить класс B в конструкторе другого класса (C), потому что вам нужно, чтобы класс (B) использовался с разрешенными зависимостями (вы начинаете использовать класс C так, как хотите, как B, но с зависимости разрешены).

Ответы на вопрос(1)

Решение Вопроса

Why putting a container in a constructor is so bad?

Я полагаю, вы хотите передать контейнер в качестве аргумента конструктора. На самом деле это вариант шаблона локатора службы, который в этом контексте считается анти-шаблоном. Есть несколько причин, по которым вы можете этого не делать.

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

Вторичный эффект, который часто встречается при использовании Service Locator, заключается в том, что вы никогда не можете быть уверены, что вы не получите исключение во время выполнения при запросе зависимостей. Каждый класс зарегистрирован правильно? Хотя некоторые контейнеры предоставляют возможность проверить, зарегистрирован ли каждый интерфейс, это не означает, что он зарегистрирован для правильного типа. Например, может случиться так, что тип будет зарегистрирован дважды в двух разных реализациях, и будет трудно заметить, может ли какой-либо фрагмент кода вызвать контейнер.

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

РЕДАКТИРОВАТЬ в свете новых разработок:

По-видимому, вы используете стороннюю библиотеку, которая полагается на ваши классы, имеющие конструктор по умолчанию. Давайте предположим, что у вас нет никакого способа повлиять на создание ваших классов и что вы должны позволить этой платформе выполнять свою работу. Имейте в виду, что это может быть большим предположением, пожалуйста, исследуйте стороннюю библиотеку для возможности сделать это в первую очередь. На первый взгляд, фреймворки, такие как ASP.NET WebForms и WCF, не дают вам много шансов, но есть способы облегчить боль в этих случаях.

I meant just to create the container in the constructor, add the respective dependency to the container and resolve the object, which can be done by simply creating instance of the dependency object and use it to create the dependent object.

Я могу что-то упустить, но зачем вам регистрировать зависимость в конструкторе? Не могли бы вы просто разрешить это в конструкторе, но зарегистрировать это где-нибудь еще? Это все равно будет локатор службы, но вы, по крайней мере, поступите неправильно.

Why doing so in the constructor is a bad idea and doing so elsewhere is fine?

Делать этоanywhere но в одном месте это плохая идея. Зачем вам распространять регистрацию контейнеров повсюду? Если вы действительно чувствуете необходимость решить, какую реализацию интерфейса использовать во время выполнения, используйте что-то вроде Factory.

Итак, почему это плохо?

the client class depends on both implementation and interface, which doesn't make it better than newing the concrete class in the constructor. the client class now also depends on the container, and the issues of Service Locator arise (see above), making now this approach worse than newing the concrete class.

Как сказал @Steven, вы теряете все преимущества Dependency Injection. Основной вопрос заключается в следующем: почему вы абсолютно хотите сделать DI в этом месте? Какие преимущества этого подхода вы хотели бы использовать? На основании ответа может быть несколько решений. Два примера из головы:

Solution 1: потерять DI для классов, создаваемых сторонней библиотекой.

Solution 2: Используйте комбинацию Bastard Injection + Service Locator. Два несправедливости могут исправить это в этом случае.

public class MyClass
{
    public MyClass()
        : this(Container.Resolve<IDependency>())
    {
    }

    public MyClass(IDependency dep)
    {
    }
}

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

 09 июл. 2012 г., 13:24
"существует зависимость от цепочки, при которой каждый объект, являющийся частью цепочки, создается вручную и передается в качестве параметра конструктора". Пожалуйста, начните новый вопрос, объясните это более подробно и объясните, почему вы не можете заставить инжектор работать. Если вы думаете, что не можете использовать инжектор конструктора, я ожидаю, что в вашем дизайне будет недостаток. Чтобы помочь вам, вам придется это объяснить.
 Ivana Stoilova09 июл. 2012 г., 14:34
Проблема в том, что мы собираемся использовать стороннее программное обеспечение, которое может полагаться на объекты, которые используют только конструкторы без параметров (по умолчанию). У нас есть объекты данных, которые являются зависимыми объектами (с чем я не согласен), и теперь нам нужно разрешить их, прежде чем передавать их стороннему коду. Так что для остальной части приложения DI подходит, но для этого конкретного случая нам нужно разрешить объекты. Почему делать это в конструкторе - плохая идея, а делать это в других местах - нормально?
 09 июл. 2012 г., 13:45
@IvanaStoilova: Какую проблему вы пытаетесь решить? Похоже, вы делаете вашу жизнь чрезвычайно сложной без всякой причины. Стивен на месте.
 09 июл. 2012 г., 13:21
Создание и настройка контейнера в каждом классе просто безумие. Это вынуждает класс снова иметь полный контроль над своими зависимостями и просто так же, как просто обновлять зависимости внутри конструктора. Вы теряете все преимущества внедрения зависимостей и делаете вещи еще более сложными, чем обычные вещи, поскольку обновление будет легче понять, чем конфигурировать контейнер, который создает эти экземпляры для вас.
 Ivana Stoilova09 июл. 2012 г., 13:15
Я имел в виду просто создать контейнер в конструкторе, добавить соответствующую зависимость в контейнер и разрешить объект, что можно сделать, просто создав экземпляр объекта зависимости и используя его для создания зависимого объекта. В этом случае это имеет значение, поскольку существует зависимость от цепочки, и создание вручную каждого объекта, являющегося частью цепочки, и передача его в качестве параметра конструктора каждому последовательному зависимому объекту может быть утомительным. Что касается сервисного локатора - в данном случае это известный анти-паттерн, но это не моя проблема.

Ваш ответ на вопрос