Зачем мне нужен контейнер IoC, а не простой код DI?

Я использовал Injection of Dependency (DI) некоторое время, впрыскивая либо в конструктор, свойство, либо метод. Я никогда не чувствовал необходимости использовать контейнер Inversion of Control (IoC). Однако, чем больше я читаю, тем больше я испытываю давление со стороны сообщества на использование контейнера IoC.

Я играл с контейнерами .NET, такими как StructureMap, NInject, Unity и Funq. Я все еще не понимаю, как контейнер IoC будет полезен/улучшит мой код.

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

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

Ответ 1

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

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());

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

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

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

  • Декларативные и вложенные транзакции базы данных
  • Декларативная и вложенная единица работы
  • Вход
  • Условия Pre/Post (Дизайн по контракту)

Ответ 2

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

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

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

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

Ответ 3

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

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

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

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

Там отличный пост в блоге Mark Seemann: Когда использовать контейнер DI

Ответ 4

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

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

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, в отличие от IMHO, более запутанных конфигурационных файлов, вы могли бы иметь что-то вроде этого:

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
}

Тогда ваш Static 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.
   }
}

Ответ 5

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

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.
}

Ответ 6

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

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

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

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

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

Ответ 7

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

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

Конечно, могут быть трудности:

  • Код, требующий сложного управления запуском/выключением/жизненным циклом, не может быть легко адаптирован к контейнеру.
  • Вам, вероятно, придется ориентироваться на любые личные, процессуальные и командные проблемы, но потом, почему вы спросили...
  • Некоторые из наборов инструментов быстро становятся тяжеловесными, поощряя такую ​​глубокую зависимость, что многие контейнеры DI начинаются с обратного хода.

Ответ 8

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

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

Преимущества для рассмотрения стороннего контейнера IoC

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

против

  • Вы получаете введенные ошибки бесплатно:)
  • Дизайн библиотеки может быть хуже вашего
  • Вам нужно изучить новый API
  • Слишком много функций, которые вы никогда не будете использовать
  • Как правило, сложнее отлаживать код, который вы не писали
  • Перенос из предыдущего контейнера IoC может быть утомительным.

Итак, взвешивайте свои плюсы против своих противников и принимайте решение.

Ответ 9

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

Значение, которое вы получите, будет зависеть от типа приложения, над которым вы работаете:

  • Для мульти-арендатора контейнер IoC может позаботиться о некотором коде инфраструктуры для загрузки разных клиентских ресурсов. Когда вам нужен компонент, специфичный для клиента, используйте настраиваемый селектор для обработки логики и не беспокойтесь об этом из кода клиента. Вы можете, конечно, создать это самостоятельно, но вот пример того, как IoC может помочь.

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

  • Если вы хотите использовать AOP для решения некоторых проблем, IoC предоставляет перехватчики для перехвата вызовов метода. Это реже делается ad-hoc для проектов, но IoC упрощает его.

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

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

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

Ответ 10

Я восстанавливаю наркоман МОК. В наши дни мне трудно обосновать использование IOC для DI в большинстве случаев. Контейнеры IOC жертвуют проверкой времени компиляции и предположительно взамен дают вам "легкую" настройку, сложное управление временем жизни и "на лету" обнаружение зависимостей во время выполнения. Я считаю, что потеря времени проверки компиляции и, в конечном итоге, магии времени/исключения, не стоит колоколов и свистов в подавляющем большинстве случаев. В крупных корпоративных приложениях им очень сложно следить за тем, что происходит.

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

Почему бы не сделать статический волшебный DI, как это:

interface IServiceA { }
interface IServiceB { }
class ServiceA : IServiceA { }
class ServiceB : IServiceB { }

class StubServiceA : IServiceA { }
class StubServiceB : IServiceB { }

interface IRoot { IMiddle Middle { get; set; } }
interface IMiddle { ILeaf Leaf { get; set; } }
interface ILeaf { }

class Root : IRoot
{
    public IMiddle Middle { get; set; }

    public Root(IMiddle middle)
    {
        Middle = middle;
    }

}

class Middle : IMiddle
{
    public ILeaf Leaf { get; set; }

    public Middle(ILeaf leaf)
    {
        Leaf = leaf;
    }
}

class Leaf : ILeaf
{
    IServiceA ServiceA { get; set; }
    IServiceB ServiceB { get; set; }

    public Leaf(IServiceA serviceA, IServiceB serviceB)
    {
        ServiceA = serviceA;
        ServiceB = serviceB;
    }
}


interface IApplicationFactory
{
    IRoot CreateRoot();
}

abstract class ApplicationAbstractFactory : IApplicationFactory
{
    protected abstract IServiceA ServiceA { get; }
    protected abstract IServiceB ServiceB { get; }

    protected IMiddle CreateMiddle()
    {
        return new Middle(CreateLeaf());
    }

    protected ILeaf CreateLeaf()
    {
        return new Leaf(ServiceA,ServiceB);
    }


    public IRoot CreateRoot()
    {
        return new Root(CreateMiddle());
    }
}

class ProductionApplication : ApplicationAbstractFactory
{
    protected override IServiceA ServiceA
    {
        get { return new ServiceA(); }
    }

    protected override IServiceB ServiceB
    {
        get { return new ServiceB(); }
    }
}

class FunctionalTestsApplication : ApplicationAbstractFactory
{
    protected override IServiceA ServiceA
    {
        get { return new StubServiceA(); }
    }

    protected override IServiceB ServiceB
    {
        get { return new StubServiceB(); }
    }
}


namespace ConsoleApplication5
{
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new ProductionApplication();
            var root = factory.CreateRoot();

        }
    }

    //[TestFixture]
    class FunctionalTests
    {
        //[Test]
        public void Test()
        {
            var factory = new FunctionalTestsApplication();
            var root = factory.CreateRoot();
        }
    }
}

Конфигурация вашего контейнера - это абстрактная реализация factory, ваши регистрации - это реализация абстрактных элементов. Если вам нужна новая синглтонная зависимость, просто добавьте другое абстрактное свойство в абстрактный factory. Если вам нужна переходная зависимость, просто добавьте другой метод и введите его как Func < > .

Преимущества:

  • Вся конфигурация создания и создания объектов централизована.
  • Конфигурация - это всего лишь код
  • Проверка времени компиляции упрощает обслуживание, так как вы не можете забыть обновлять свои регистрационные данные.
  • Магия отражения во время выполнения

Я рекомендую скептикам дать ему следующий проект зеленого поля и честно спросить себя, в какой момент вам нужен контейнер. Легко включить в контейнер IOC позже, поскольку вы просто заменяете реализацию factory конфигурационным модулем IOC Container.

Ответ 11

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

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

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

Есть много других преимуществ для контейнеров DI/IoC, поскольку другие люди подробно описывают здесь, и переход от идеи создания объектов к запрашивающим объектам может быть изгиб ума, но использование DI было очень полезно для меня и моей команды, поэтому, возможно, вы можете добавить его в свой арсенал!

Ответ 12

Рамки IoC превосходны, если вы хотите...

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

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

  • ... напишите crappy code с нечеткими зависимостями, которые трудно реорганизовать, так как вы никогда не узнаете, что от них зависит.

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

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

что явно ошибочно, поскольку зависимость между Foo и Bar жестко связана. Затем они поняли, что лучше написать код, например

public class Foo {
    public IBar Apa {get;set;}
    Foo() {
        Apa = IoC<IBar>();
    }
}

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

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

Правильное решение будет выглядеть как

data Foo = Foo { apa :: Bar }

или, возможно,

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

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

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

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

Ответ 13

Я обнаружил, что правильное внедрение Dependency Injection имеет тенденцию заставлять программистов использовать множество других методов программирования, которые помогают улучшить тестируемость, гибкость, удобство обслуживания и масштабируемость кода: такие практики, как принцип единой ответственности, разделение проблем, и кодирование против 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, обратите внимание, что:

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

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

Ответ 14

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

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

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

Ответ 15

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

Ответ 16

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

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

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

Ответ 17

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

Ответ 18

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

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

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

Ответ 19

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

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

Ответ 20

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

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

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

Ответ 21

Итак, прошло почти три года, а?

  • 50% тех, кто высказался в поддержку IoC-фреймворков, не понимают разницы между IoC и IoC Framework. Я сомневаюсь, что они знают, что вы можете писать код без развертывания на сервере приложений.

  • Если мы возьмем самую популярную инфраструктуру Java Spring, это конфигурация IoC переместилась из XMl в код и теперь выглядит как

`@Configuration открытый класс AppConfig {

public @Bean TransferService transferService() {
    return new TransferServiceImpl(accountRepository());
}

public @Bean AccountRepository accountRepository() {
    return new InMemoryAccountRepository();
}

} ` И нам нужна основа для этого, почему именно?

Ответ 22

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

Если вы используете его только для упрощения построения объекта, я должен спросить: вы создаете экземпляр этого объекта в нескольких местах? Будет ли синглтон не соответствовать вашим потребностям? Вы меняете конфигурацию во время выполнения? (Переключение типов источников данных и т.д.).

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

Кто сказал, что интерфейс лучше, чем наследование? Скажем, вы тестируете Сервис. Почему бы не использовать конструктор DI и создать mocks зависимостей с использованием наследования? Большинство сервисов, которые я использую, имеют только несколько зависимостей. Тестирование модуля таким образом предотвращает поддержание тонны бесполезных интерфейсов и означает, что вам не нужно использовать Resharper для быстрого поиска объявления метода.

Я считаю, что для большинства реализаций, говоря, что IoC Containers удаляет ненужный код, это миф.

Сначала создайте контейнер в первую очередь. Затем вам необходимо определить каждый объект, который необходимо инициализировать. Таким образом, вы не сохраняете код при инициализации, вы его перемещаете (если ваш объект не используется более одного раза. Лучше ли он как Singleton?). Затем для каждого объекта, который вы инициализировали таким образом, вам необходимо создать и поддерживать интерфейс.

У кого-нибудь есть мысли по этому поводу?

Ответ 23

Вам понадобится контейнер IoC, если вам необходимо централизовать конфигурацию ваших зависимостей, чтобы их можно было легко поменять на массу. Это имеет наибольший смысл в TDD, где меняются многие зависимости, но там, где существует небольшая взаимозависимость между зависимостями. Это делается за счет того, что в некоторой степени запутывает поток контроля над конструкцией объекта, поэтому важно иметь хорошо организованную и обоснованную документацию. Также хорошо иметь повод сделать это, в противном случае это просто абстракция золотым покрытием. Я видел, как это делалось так плохо, что оно было перенесено в эквивалент инструкции goto для конструкторов.

Ответ 24

Вот почему. Проект называется IOC-with-Ninject. Вы можете загрузить и запустить его с помощью Visual Studio. В этом примере используется Ninject, но ВСЕ "новые" операторы находятся в одном месте, и вы можете полностью изменить, как работает ваше приложение, изменив используемый модуль привязки. Пример настроен таким образом, что вы можете привязываться к издевавшейся версии служб или реальной версии. В небольших проектах, которые могут не иметь значения, но в крупных проектах это большое дело.

Чтобы быть ясными, преимущества, которые я вижу в них: 1) ВСЕ новые операторы в одном месте в корневом коде. 2) Полностью пересчет кода с одним изменением. 3) Дополнительные очки за "классный фактор", потому что это... хорошо: круто.: Р

Ответ 25

Я постараюсь найти, почему IOC может не понравиться с моей точки зрения.

Как и во всем остальном, контейнер IOC (или, как сказал Эйнштейн я = OC ^ 2), представляет собой концепцию, которую вы должны решить самостоятельно, если она вам нужна или нет в вашем коде. Недавний модный протест в отношении МОК - это только мода. Не поддавайтесь моде, это прежде всего. Есть множество концепций, которые вы можете реализовать в своем коде. Прежде всего, я использую инъекцию зависимостей, так как я начал программировать, и узнал сам термин, когда он был популяризирован под этим именем. Контроль зависимостей - очень старая тема, и до сих пор она была рассмотрена триллионами способов, в зависимости от того, что отделялось от чего. Развязать все от всего - это вздор. Проблема с контейнером IOC заключается в том, что он пытается быть таким же полезным, как Entity Framework или NHibernate. При написании объектно-реляционного картографа просто необходимо, как только вам придется связывать любую базу данных с вашей системой, контейнер IOC не всегда необходим. Поэтому, когда контейнер IOC полезен:

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

1: Это не так часто, что у вас так много зависимостей в вашем коде или что вы знаете о них в начале дизайна. Абстрактное мышление полезно при абстрактном мышлении.

2: Совмещение кода с сторонним кодом - это проблема HuGe. Я работал с кодом, которому уже 10+ лет, и в то время это были фантастические и продвинутые концепции ATL, COM, COM + и т.д. Теперь с этим кодом вы ничего не можете сделать. Я говорю, что продвинутая концепция дает очевидное преимущество, но это отменяется в долгосрочной перспективе с использованием самого устаревшего преимущества. Это просто сделало все это более дорогостоящим.

3: Разработка программного обеспечения достаточно сложна. Вы можете расширить его до неузнаваемых уровней, если вы позволите некоторым расширенным понятиям обрезать ваш код. Проблема с IOC2. Хотя это развязка зависимостей, она также развязывает логический поток. Представьте, что вы нашли ошибку, и вам нужно установить перерыв, чтобы изучить ситуацию. IOC2, как и любая другая передовая концепция, затрудняет это. Исправление ошибки внутри концепции сложнее, чем исправление ошибки в более понятном коде, потому что, когда вы исправляете ошибку, концепция должна выполняться снова. (Чтобы дать вам пример, С++.NET постоянно меняет синтаксис настолько, что вам нужно много думать, прежде чем реорганизовать какую-то более старую версию .NET.) Так в чем же проблема с IOC? Проблема заключается в разрешении зависимостей. Логика разрешения обычно скрыта в самой IOC2, написанной, может быть, необычным образом, что вам нужно учиться и поддерживать. Будет ли ваш сторонний продукт через 5 лет? Microsoft не была.

"Мы знаем, как" синдром "пишется повсюду в отношении IOC2. Это похоже на тестирование автоматизации. Причудливый термин и идеальное решение на первый взгляд, вы просто ставите все свои тесты на ночь и видите результаты утром. Очень больно объяснять компании после компании, что на самом деле означает автоматическое тестирование. Автоматическое тестирование - это, безусловно, не быстрый способ уменьшить количество ошибок, которые вы можете ввести за одну ночь, чтобы повысить качество вашего продукта. Но мода делает это понятие досадно доминирующим. IOC2 страдает одним и тем же синдромом. Считается, что вам нужно реализовать его, чтобы ваше программное обеспечение было хорошим. Последнее интервью EvErY меня спросили, реализую ли я IOC2 и автоматизацию. Это признак моды: у компании была часть кода, написанная в MFC, которую они не оставят.

Вам необходимо изучить IOC2 как любую другую концепцию программного обеспечения. Решение, если IOC2 необходимо использовать, входит в состав команды и компании. Однако, прежде чем решение будет принято, следует упомянуть хотя бы ВСЕ приведенные выше аргументы. Только если вы видите, что плюс сторона перевешивает негативную сторону, вы можете принять положительное решение.

В IOC2 нет ничего плохого, за исключением того, что он решает только проблемы, которые он решает, и вводит проблемы, которые он представляет. Ничего больше. Однако идти против моды очень сложно, у них есть пот, последователи чего-либо. Странно, как никто из них не существует, когда проблема с их причудой становится очевидной. Многие концепции в индустрии программного обеспечения были защищены, потому что они создают прибыль, книги написаны, проводятся конференции, выпускаются новые продукты. Это мода, обычно недолговечная. Как только люди находят что-то еще, они полностью отказываются от него. IOC2 полезен, но он показывает те же признаки, что и многие другие исчезнувшие концепции, которые я видел. Я не знаю, выживет ли он. Для этого нет правила. Вы думаете, что если это полезно, оно выживет. Нет, это не так. Одной большой богатой компании достаточно, и концепция может умереть в течение нескольких недель. Посмотрим. NHibernate выжил, EF занял второе место. Возможно, IOC2 тоже выживет. Не забывайте, что большинство концепций в разработке программного обеспечения ничего особенного не имеют, они очень логичны, просты и очевидны, и иногда сложнее запомнить текущее соглашение об именах, чем понимать само понятие. Знает ли IOC2 разработчика лучше разработчика? Нет, потому что, если разработчику не удалось придумать концепцию, похожую по характеру на IOC2, то ему или ей будет трудно понять, какая проблема IOC2 решает, используя ее будет выглядеть искусственно, и он или она может начать использовать ее ради того, чтобы быть каким-то политически правильным.

Ответ 26

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

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

Ответ 29

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

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

1) Наличие библиотеки сторонних разработчиков для управления временем жизни ваших объектов "автоматически" может принести неожиданные результаты. Мы обнаружили, что особенно в крупных проектах у вас может быть гораздо больше копий объекта, чем вы ожидаете, и больше, чем если бы вы вручную управляли жизненными циклами. Я уверен, что это зависит от используемой структуры, но проблема существует, тем не менее. Это также может быть проблематичным, если ваш объект содержит ресурсы, подключения к данным и т.д., Так как объект может жить дольше, чем вы ожидаете. Таким образом, контейнеры IoC неизбежно увеличивают использование ресурсов и объем памяти приложения.

2) Контейнеры IoC, на мой взгляд, являются формой "программирования черного ящика". Я обнаружил, что, в частности, наши менее опытные разработчики склонны злоупотреблять ими. Это позволяет программисту не думать о том, как объекты должны относиться друг к другу или как их отделять, потому что он предоставляет им механизм, в котором они могут просто захватить любой объект, который они хотят из воздуха. Например, может быть хорошая причина дизайна, что ObjectA никогда не должен знать об ObjectB напрямую, но вместо создания factory или локатора моста или службы неопытный программист просто скажет: "Нет проблем, я просто возьму ObjectB из Контейнер IoC". Это может привести к усилению связи объектов, что, как предполагается, предотвратит IoC.

Ответ 30

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