Зачем мне контейнер IoC, а не простой DI-код? [закрыто]

Использование объекта отлично работает в обоих направлениях ... Этот метод имеет немного избыточного десериализации исходного значения в два раза.

ObjectMapper mapper = new ObjectMapper();
RawJsonValue value = new RawJsonValue();
value.setRawValue(new RawHello(){{this.data = "universe...";}});
String json = mapper.writeValueAsString(value);
System.out.println(json);
RawJsonValue result = mapper.readValue(json, RawJsonValue.class);
json = mapper.writeValueAsString(result.getRawValue());
System.out.println(json);
RawHello hello = mapper.readValue(json, RawHello.class);
System.out.println(hello.data);

RawHello.java

public class RawHello {

    public String data;
}

RawJsonValue.java

public class RawJsonValue {

    private Object rawValue;

    public Object getRawValue() {
        return rawValue;
    }

    public void setRawValue(Object value) {
        this.rawValue = value;
    }
}
598
задан Vadim 17 October 2013 в 19:39
поделиться

21 ответ

Вау, не могу поверить, что Джоэл предпочел бы это:

var svc = new ShippingService(new ProductLocator(), 
   new PricingService(), new InventoryService(), 
   new TrackingRepository(new ConfigProvider()), 
   new Logger(new EmailLogger(new ConfigProvider())));

этому:

var svc = IoC.Resolve<IShippingService>();

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

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


Хорошо, давайте еще больше оправдаем это. Допустим, у вас есть некоторые сущности или объекты модели, которые вы хотите привязать к интеллектуальному пользовательскому интерфейсу. Этот умный пользовательский интерфейс (назовем его Shindows Morms) требует, чтобы вы реализовали INotifyPropertyChanged, чтобы он мог отслеживать изменения и соответствующим образом обновлять пользовательский интерфейс.

«Хорошо, это звучит не так сложно», поэтому вы начинаете писать.

Вы начинаете так:

public class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime CustomerSince { get; set; }
    public string Status { get; set; }
}

.. и заканчиваете этим :

public class UglyCustomer : INotifyPropertyChanged
{
    private string _firstName;
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            string oldValue = _firstName;
            _firstName = value;
            if(oldValue != value)
                OnPropertyChanged("FirstName");
        }
    }

    private string _lastName;
    public string LastName
    {
        get { return _lastName; }
        set
        {
            string oldValue = _lastName;
            _lastName = value;
            if(oldValue != value)
                OnPropertyChanged("LastName");
        }
    }

    private DateTime _customerSince;
    public DateTime CustomerSince
    {
        get { return _customerSince; }
        set
        {
            DateTime oldValue = _customerSince;
            _customerSince = value;
            if(oldValue != value)
                OnPropertyChanged("CustomerSince");
        }
    }

    private string _status;
    public string Status
    {
        get { return _status; }
        set
        {
            string oldValue = _status;
            _status = value;
            if(oldValue != value)
                OnPropertyChanged("Status");
        }
    }

    protected virtual void OnPropertyChanged(string property)
    {
        var propertyChanged = PropertyChanged;

        if(propertyChanged != null)
            propertyChanged(this, new PropertyChangedEventArgs(property));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

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

Вы когда-нибудь слышали этот термин, работайте умнее, а не усерднее?

Представьте себе, что какой-то умный парень из вашей команды подошел и сказал: «Вот способ попроще»

Если вы сделайте свои свойства виртуальными (успокойтесь, это не такая уж большая проблема), тогда мы сможем автоматически включить в поведение этого свойства. (Это называется АОП, но не беспокойтесь о названии, сосредоточьтесь на том, что он сделает для вас)

В зависимости от того, какой инструмент IoC вы используете, вы можете сделать что-то похожее на это:

var bindingFriendlyInstance = IoC.Resolve<Customer>(new NotifyPropertyChangedWrapper());

Уф! Вся эта ручная BS INotifyPropertyChanged теперь автоматически генерируется для вас, на каждом средстве настройки виртуальных свойств рассматриваемого объекта.

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

Некоторые другие интересные применения инструмента IoC для АОП:

  • Декларативные и вложенные транзакции базы данных
  • Декларативные и вложенные единицы работы
  • Ведение журнала
  • Предварительно / Условия публикации (Дизайн по контракту)
441
ответ дан 22 November 2019 в 22:01
поделиться

В мире .NET АОП не слишком популярен, поэтому для DI реальный вариант - независимо от того, пишете ли вы его самостоятельно или используете другой фреймворк.

Если вы использовали АОП вы можете внедрить при компиляции приложения, что чаще встречается в Java.

У DI есть много преимуществ, таких как уменьшение связи, что упрощает модульное тестирование, но как вы его реализуете? Вы хотите использовать отражение, чтобы сделать это самостоятельно?

2
ответ дан 22 November 2019 в 22:01
поделиться

Использование контейнера в основном связано с изменением императивного / скриптового стиля инициализации и конфигурации на декларативный . Это может иметь несколько различных положительных эффектов:

  • Сокращение волосяного комка подпрограмм запуска основной программы.
  • Обеспечение достаточно глубоких возможностей реконфигурации во время развертывания.
  • Превращение стиля с внедрением зависимостей в путь наименьшего количества сопротивление новой работе.

Конечно, могут возникнуть трудности:

  • Код, требующий сложного управления запуском / остановкой / жизненным циклом, может быть нелегко адаптировать к контейнеру.
  • Вам, вероятно, придется перемещаться по любому личному проблемы процесса и культуры команды - но тогда, вот почему вы спросили ...
  • Некоторые наборы инструментов сами быстро становятся тяжеловесными, поощряя своего рода глубокую зависимость, которую многие контейнеры DI начинали как обратная реакция.
27
ответ дан 22 November 2019 в 22:01
поделиться

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

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

... или оба верны в той или иной степени.

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

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

28
ответ дан 22 November 2019 в 22:01
поделиться

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

8
ответ дан 22 November 2019 в 22:01
поделиться

I think most of the value of an IoC is garnered by using DI. Since you are already doing that, the rest of the benefit is incremental.

The value you get will depend on the type of application you are working on:

  • For multi-tenant, the IoC container can take care of some of the infrastructure code for loading different client resources. When you need a component that is client specific, use a custom selector to do handle the logic and don't worry about it from your client code. You can certainly build this yourself but here's an example of how an IoC can help.

  • With many points of extensibility, the IoC can be used to load components from configuration. This is a common thing to build but tools are provided by the container.

  • If you want to use AOP for some cross-cutting concerns, the IoC provides hooks to intercept method invocations. This is less commonly done ad-hoc on projects but the IoC makes it easier.

I've written functionality like this before but if I need any of these features now I would rather use a pre-built and tested tool if it fits my architecture.

As mentioned by others, you can also centrally configure which classes you want to use. Although this can be a good thing, it comes at a cost of misdirection and complication. The core components for most applications aren't replaced much so the trade-off is a little harder to make.

I use an IoC container and appreciate the functionality but have to admit that I've noticed the trade-off: My code becomes more clear at the class level and less clear at the application level (i.e. visualizing control flow).

17
ответ дан 22 November 2019 в 22:01
поделиться

На мой взгляд, преимущество IoC номер один - это возможность централизовать конфигурацию ваших зависимостей.

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

public class CustomerPresenter
{
  public CustomerPresenter() : this(new CustomerView(), new CustomerService())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

] Если вы использовали статический класс IoC, в отличие от, ИМХО, более запутанных файлов конфигурации, у вас могло бы быть что-то вроде этого:

public class CustomerPresenter
{
  public CustomerPresenter() : this(IoC.Resolve<ICustomerView>(), IoC.Resolve<ICustomerService>())
  {}

  public CustomerPresenter(ICustomerView view, ICustomerService service)
  {
    // init view/service fields
  }
  // readonly view/service fields
}

Тогда ваш статический класс IoC будет выглядеть так, я использую здесь Unity.

public static IoC
{
   private static readonly IUnityContainer _container;
   static IoC()
   {
     InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerView, CustomerView>();
      _container.RegisterType<ICustomerService, CustomerService>();
      // all other RegisterTypes and RegisterInstances can go here in one file.
      // one place to change dependencies is good.
   }
}
32
ответ дан 22 November 2019 в 22:01
поделиться

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

public void GetPresenter()
{
    var presenter = new CustomerPresenter(new CustomerService(new CustomerRepository(new DB())));
}

class CustomerPresenter
{
    private readonly ICustomerService service;
    public CustomerPresenter(ICustomerService service)
    {
        this.service = service;
    }
}

class CustomerService
{
    private readonly IRespository<Customer> repository;
    public CustomerService(IRespository<Customer> repository)
    {
        this.repository = repository;
    }
}

class CustomerRepository : IRespository<Customer>
{
    private readonly DB db;
    public CustomerRepository(DB db)
    {
        this.db = db;
    }
}

class DB { }

Если у вас были загружены все эти зависимости и контейнер IoC, вы могли бы разрешить CustomerService, и все дочерние зависимости будут автоматически разрешены.

Например:

public static IoC
{
   private IUnityContainer _container;
   static IoC()
   {
       InitializeIoC();
   }

   static void InitializeIoC()
   {
      _container = new UnityContainer();
      _container.RegisterType<ICustomerService, CustomerService>();
      _container.RegisterType<IRepository<Customer>, CustomerRepository>();
   }

   static T Resolve<T>()
   {
      return _container.Resolve<T>();
   }
}

public void GetPresenter()
{
   var presenter = IoC.Resolve<CustomerPresenter>();
   // presenter is loaded and all of its nested child dependencies 
   // are automatically injected
   // -
   // Also, note that only the Interfaces need to be registered
   // the concrete types like DB and CustomerPresenter will automatically 
   // resolve.
}
32
ответ дан 22 November 2019 в 22:01
поделиться

Я с тобой, Вадим. Контейнеры IoC используют простую, элегантную и полезную концепцию и превращают ее в то, что вы должны изучить в течение двух дней с помощью руководства на 200 страниц.

Я лично озадачен тем, как сообщество IoC восприняло красивый, элегантный статью Мартина Фаулера и превратил ее в кучу сложных фреймворков, обычно с руководствами на 200-300 страниц.

Я стараюсь не осуждать (ХАХА!), но я думаю, что люди, использующие контейнеры IoC, (A ) очень умны и (Б) не сочувствуют людям, которые не так умны, как они. Для них все имеет смысл, поэтому им трудно понять, что многие простые программисты сочтут эти концепции запутанными. Это проклятие знания . Людям, разбирающимся в контейнерах IoC, трудно поверить, что есть люди, которые этого не понимают.

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

Я считаю, что если вы используете контейнеры IoC, ваш код станет, честно говоря, намного труднее читать. Количество мест, на которые нужно смотреть, чтобы понять, что пытается сделать код, увеличивается как минимум на одно. И где-то на небе кричит ангел.

138
ответ дан 22 November 2019 в 22:01
поделиться

Мне кажется, вы уже создали свой собственный контейнер IoC (используя различные шаблоны, описанные Мартином Фаулером) и спрашиваете, почему чья-то реализация лучше, чем ваш.

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

Плюсы для рассмотрения стороннего контейнера IoC

  • Вы получаете исправление ошибок бесплатно
  • Дизайн библиотеки может быть лучше, чем у вас
  • Люди могут быть уже знакомы с конкретной библиотекой
  • Библиотека может быть быстрее вашей
  • В ней могут быть некоторые функции, которые вы хотели бы реализовать, но у вас никогда не было времени (у вас есть средство поиска служб?)

Минусы

  • Вы бесплатно получаете сообщения об ошибках:
23
ответ дан 22 November 2019 в 22:01
поделиться

По-видимому, никто не заставляет вас использовать инфраструктуру контейнера DI. Вы уже используете DI для разделения классов и улучшения тестируемости, поэтому вы получаете множество преимуществ. Короче говоря, вы предпочитаете простоту, что, как правило, хорошо.

Если ваша система достигает уровня сложности, когда DI вручную становится рутиной (то есть увеличивает объем обслуживания), сравните это с кривой обучения команды Каркас контейнера DI.

Если вам нужен больший контроль над управлением временем жизни зависимостей (то есть, если вы чувствуете необходимость реализовать шаблон Singleton), посмотрите на контейнеры DI.

Если вы используете контейнер DI, используйте только те функции, которые вам нужны. Пропустите файл конфигурации XML и настройте его в коде, если этого достаточно. Придерживайтесь внедрения конструктора. Основы Unity или StructureMap можно сократить до пары страниц.

На эту тему есть отличная запись в блоге Марка Симанна: Когда использовать контейнер DI

37
ответ дан 22 November 2019 в 22:01
поделиться

Внедрение зависимостей в проект ASP.NET может быть выполнено с помощью нескольких строк кода. Я полагаю, что есть некоторое преимущество в использовании контейнера, когда у вас есть приложение, которое использует несколько интерфейсов и нуждается в модульных тестах.

-8
ответ дан 22 November 2019 в 22:01
поделиться

Как раз так случилось, что я выдергивал самодельный код DI и заменял его на IOC. Я, вероятно, удалил более 200 строк кода и заменил его примерно на 10. Да, мне пришлось немного узнать, как использовать контейнер (Winsor), но я инженер, работающий над интернет-технологиями в 21 век, так что я к этому привык. Я, наверное, потратил около 20 минут, просматривая инструкции. Это стоило моего времени.

6
ответ дан 22 November 2019 в 22:01
поделиться

Лично я использую IoC как своего рода структурную карту моего приложения (да, я также предпочитаю StructureMap;)). Это позволяет легко заменить мои обычные реализации интерфейса реализациями Moq во время тестов. Создание тестовой установки может быть таким же простым, как создание нового вызова init к моей IoC-структуре, заменяя любой класс, который является моей тестовой границей, макетом.

Вероятно, это не то, для чего существует IoC, но это то, что я обнаружил, что использую его больше всего ..

0
ответ дан 22 November 2019 в 22:01
поделиться

По мере того как вы продолжаете отделять классы и инвертировать зависимости, классы продолжают оставаться небольшими, а «граф зависимостей» продолжает расти в размерах. (Это неплохо.) Использование основных функций контейнера IoC делает подключение всех этих объектов тривиальным, но выполнение этого вручную может оказаться очень обременительным. Например, что, если я хочу создать новый экземпляр «Foo», но ему нужен «Бар». А «Бар» нужны «А», «В» и «С». И каждому из них нужны еще 3 вещи и т.д. и т.п. (да, я не могу придумать хороших фальшивых имен :)).

Использование контейнера IoC для построения графа объектов для вас упрощает тонну и подталкивает его в одноразовую конфигурацию. Я просто говорю «создай мне фу», и он выясняет, что

5
ответ дан 22 November 2019 в 22:01
поделиться

Каждый раз, когда вы используете ключевое слово "new", вы создаете конкретную зависимость класса, и в вашей голове должен прозвучать сигнал тревоги. Становится труднее тестировать этот объект изолированно. Решение состоит в том, чтобы запрограммировать интерфейсы и внедрить зависимость, чтобы объект можно было модульно тестировать со всем, что реализует этот интерфейс (например, имитирует).

Проблема в том, что вам нужно где-то конструировать объекты. Шаблон Factory - это один из способов избавиться от связывания ваших POXO (Plain Old "вставьте здесь свой объектно-ориентированный язык" Объекты). Если вы и ваши коллеги пишете такой код, то контейнер IoC - это следующее «дополнительное улучшение», которое вы можете внести в свою кодовую базу. Он уберет весь этот неприятный шаблонный код Factory из ваших чистых объектов и бизнес-логики. Oни' Я получу это и полюблю это. Черт возьми, расскажите компании, почему вам это нравится, и вызовите у всех энтузиазм.

Если ваши коллеги еще не занимаются DI, то я предлагаю вам сначала сосредоточиться на этом. Расскажите, как писать чистый код, который легко тестировать. Чистый код DI - это сложная часть, как только вы там окажетесь, перенос логики подключения объектов из классов Factory в контейнер IoC должен быть относительно тривиальным.

9
ответ дан 22 November 2019 в 22:01
поделиться

То же о Unity. Если стать слишком большим, можно услышать скрип стропил.

Меня никогда не удивляет, когда люди начинают сплетничать о том, насколько чистым выглядит код IoC, - это те же люди, которые когда-то говорили о том, что шаблоны на C ++ были элегантным способом вернуться в 90-е, но в настоящее время осуждают их как загадочные. Ба!

4
ответ дан 22 November 2019 в 22:01
поделиться

Фреймворки IoC отлично подходят, если вы хотите ...

  • ... выбросить безопасность типов. Многие (все?) Фреймворки IoC заставляют вас выполнять код, если вы хотите быть уверенным, что все подключено правильно. «Эй! Надеюсь, у меня все настроено, поэтому мои инициализации этих 100 классов не потерпят неудачу в производстве, выбрасывая исключения с нулевым указателем!»

  • ... засоряйте ваш код глобальными переменными (фреймворки IoC все об изменении глобальных состояний).

  • ... писать дрянной код с неясными зависимостями, который сложно рефакторировать, поскольку вы никогда не узнаете, что от чего зависит.

Проблема с IoC в том, что люди, которые их используют, использовали написать такой код

public class Foo {
    public Bar Apa {get;set;}
    Foo() {
        Apa = new Bar();
    }
}

, который явно ошибочен, поскольку зависимость между Foo и Bar жестко запрограммирована. В Haskell тип Foo () будет IO Foo , но вам действительно не нужна часть IO , и это должно быть предупреждающим знаком, что что-то неправильно с вашим дизайном, если вы его поняли.

Чтобы избавиться от него (IO-часть), получить все преимущества IoC-frameworks и ни одного из его недостатков, вы можете вместо этого использовать абстрактную фабрику.

Правильный решением будет что-то вроде

data Foo = Foo { apa :: Bar }

или, может быть,

data Foo = forall b. (IBar b) => Foo { apa :: b }

и ввести (но я бы не назвал это инъекцией) Bar.

Также: см. это видео с Эриком Мейером (изобретателем LINQ), где он говорит, что DI предназначен для люди, не разбирающиеся в математике (и я не могу с этим согласиться): http://www.youtube.com/watch?v=8Mttjyf-8P4

В отличие от г-на Спольски, я не верю, что люди, которые используют IoC-frameworks, очень умны - я просто считаю, что они не знают математики.

14
ответ дан 22 November 2019 в 22:01
поделиться

Самым большим преимуществом использования контейнеров IoC для меня (лично я использую Ninject) было исключение передачи настроек и других типов объектов глобального состояния.

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

Без использования контейнера IoC мне пришлось бы передавать настройки и / или метаданные через 2, 3, ...,

14
ответ дан 22 November 2019 в 22:01
поделиться

Вам не нужен контейнер IoC.

Но если вы строго следуете шаблону DI, вы обнаружите, что его наличие уберет тонну избыточного, скучного кода.

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

7
ответ дан 22 November 2019 в 22:01
поделиться

Я обнаружил, что правильная реализация внедрения зависимостей заставляет программистов использовать множество других методов программирования, которые помогают улучшить тестируемость, гибкость, ремонтопригодность и масштабируемость кода: такие практики, как Принцип единой ответственности, разделение проблем и кодирование в соответствии с API. Такое ощущение, что я вынужден писать больше модульных классов и методов небольшого размера, что упрощает чтение кода, потому что его можно обрабатывать небольшими фрагментами.

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

var merger = new SimpleWorkflowInstanceMerger(
    new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName), 
    new WorkflowAnswerRowUtil(
        new WorkflowFieldAnswerEntMapper(),
        new ActivityFormFieldDisplayInfoEntMapper(),
        new FieldEntMapper()),
    new AnswerRowMergeInfoRepository());

Оглядываясь назад, это было бы быстрее использовать фреймворк IoC, поскольку модули определяют почти все эти вещи условно.

Потратив некоторое время на изучение ответов и комментариев по этому вопросу, я убежден, что люди, которые выступают против использования контейнера IoC, не практикуют истинную инъекцию зависимостей. Я видел примеры практик, которые обычно путают с внедрением зависимостей.Некоторые люди жалуются на трудности с «чтением» кода. Если все сделано правильно, подавляющая часть вашего кода должна быть идентична при использовании DI вручную, как при использовании контейнера IoC. Разница должна полностью заключаться в нескольких «точках запуска» внутри приложения.

Другими словами, если вам не нравятся контейнеры IoC, вы, вероятно, не выполняете внедрение зависимостей так, как предполагалось.

Еще один момент: внедрение зависимостей действительно невозможно сделать вручную, если вы где-нибудь используете отражение. Хотя я ненавижу то, что отражение делает с навигацией по коду, вы должны признать, что есть определенные области, где этого действительно нельзя избежать. Например, ASP.NET MVC пытается создать экземпляр контроллера посредством отражения каждого запроса. Чтобы выполнить инъекцию зависимостей вручную, вам нужно сделать каждый контроллер «корнем контекста», например:

public class MyController : Controller
{
    private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
    public MyController()
    {
        _simpleMerger = new SimpleWorkflowInstanceMerger(
            new BitFactoryLog(typeof(SimpleWorkflowInstanceMerger).FullName), 
            new WorkflowAnswerRowUtil(
                new WorkflowFieldAnswerEntMapper(),
                new ActivityFormFieldDisplayInfoEntMapper(),
                new FieldEntMapper()),
            new AnswerRowMergeInfoRepository())
    }
    ...
}

Теперь сравните это с разрешением фреймворка DI делать это за вас:

public MyController : Controller
{
    private readonly ISimpleWorkflowInstanceMerger _simpleMerger;
    public MyController(ISimpleWorkflowInstanceMerger simpleMerger)
    {
        _simpleMerger = simpleMerger;
    }
    ...
}

Использование фреймворк DI, обратите внимание:

  • Я могу выполнить модульное тестирование этого класса. Создав макет ISimpleWorkflowInstanceMerger , я могу проверить, используется ли он так, как я предполагаю, без необходимости подключения к базе данных или чего-то еще.
  • Я использую гораздо меньше кода, и его гораздо легче читать.
  • Если изменяется одна из моих зависимостей, мне не нужно вносить какие-либо изменения в контроллер. Это особенно приятно, если учесть, что несколько контроллеров могут использовать одни и те же зависимости.
  • Я никогда явно не ссылаюсь на классы из моего уровня данных.Мое веб-приложение может просто включать ссылку на проект, содержащий интерфейс ISimpleWorkflowInstanceMerger . Это позволяет мне разбивать приложение на отдельные модули и поддерживать настоящую многоуровневую архитектуру, что, в свою очередь, делает вещи более гибкими.

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

10
ответ дан 22 November 2019 в 22:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: