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

Я использую Инъекции Зависимостей (DI) некоторое время, вводя либо в конструктор, свойство, либо в метод. Я никогда не чувствовал необходимости использовать инверсия управления контейнер (МОК). Тем не менее, чем больше я читаю, тем больше давления я чувствую от сообщества, чтобы использовать контейнер IoC.

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

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

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

30 ответов


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

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

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

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

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

если вы делаете виртуальные свойства (успокойтесь, это не так уж важно), то мы можем плетение в этом поведении собственность автоматически. (Вот называется AOP, но не беспокойтесь о названии, сосредоточьтесь на том, что он собирается сделать для вас)

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

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

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

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

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

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

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

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

Я стараюсь не осуждать (ха-ха!), но я думаю, что люди, которые используют контейнеры МОК (A) очень умный и (Б) лишенный сочувствия к людям, которые не так умны, как они. Для них все имеет смысл, поэтому им трудно понять, что многие обычные программисты найдут эти понятия запутанными. Это проклятие знания. Люди, которые понимают контейнеры МОК, с трудом верят, что есть люди, которые этого не понимают.

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

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


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

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

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

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

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


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

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

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
}

тогда ваш статический класс 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.
   }
}

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

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

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

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

...или и то и другое в той или иной степени верно.

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

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


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

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

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

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

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

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

плюсы для рассмотрения стороннего контейнера МОК

  • вы получаете исправлены ошибки бесплатно!--12-->
  • дизайн библиотеки может быть лучше, чем ваша
  • люди могут быть знакомы с конкретной библиотекой
  • библиотека может быть быстрее, чем ваша
  • у него могут быть некоторые функции, которые вы хотите реализовать, но никогда не было времени (у вас есть служба locater?)

минусы

  • вы получаете ошибки введены, бесплатно:)
  • дизайн библиотеки может быть хуже, чем у тебя!--12-->
  • вы должны изучить новый API
  • слишком много функций, вы никогда не будете использовать
  • обычно сложнее отлаживать код, который вы не написали
  • миграция из предыдущего контейнера IoC может быть утомительной

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


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

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

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

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

  • Если вы хотите использовать AOP для некоторых сквозных проблем,МОК предоставляет крючки перехватить метод вызовы. Это реже делается ad-hoc по проектам, но МОК делает это проще.

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

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

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


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

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

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

плюсы:

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

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


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

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

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

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


рамки МОК превосходны, если вы хотите...

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

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

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

проблема с 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-часть и должна быть предупреждающим знаком, что что-то не так с вашим дизайном, если вы его получили.

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

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

data Foo = Foo { apa :: Bar }

или может быть!--8-->

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

и inject (но я бы не назвал его inject) Bar.

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

в отличие от г-на Спольского я не считаю, что люди, которые используют МОК - фреймворки, очень умны-я просто считаю, что они не знают математики.


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

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

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

в ретроспективе было бы быстрее использовать структуру 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 framework сделать это за вас:

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

используя di framework, обратите внимание, что:

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

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


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

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

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


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


не нужно IoC-контейнер.

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

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


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


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

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

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


Dittos о единстве. Становишься слишком большим, и слышно, как скрипят стропила.

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


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

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

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


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

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

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

} ` И нам нужна структура, чтобы сделать это, почему именно?


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

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

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

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

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

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

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


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


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

просто быть ясно, преимущества, как я их вижу: 1) все новая операторы в одном месте в корне кода. 2) полностью код ре-фактора с одним изменением. 3) дополнительные очки для крутости, потому что это... что ж, круто. : p


Я постараюсь найти, почему МОК не может быть хорошим с моей точки зрения.

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

  1. когда у вас есть ситуация со многими зависимостями вы хотите организовать
  2. когда вы не заботитесь о соединении вашего кода со сторонним продуктом
  3. когда ваши разработчики хотят научиться работать с новым инструментом

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

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

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

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

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

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


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

Это, вероятно, не то, для чего существует МОК, но это то, что я использую его для большинства..


КОНТЕЙНЕР IOC РЕШАЕТ ПРОБЛЕМУ, КОТОРУЮ ВЫ, ВОЗМОЖНО, НЕ ИМЕЕТЕ, НО ЭТО ХОРОШАЯ ПРОБЛЕМА, ЧТОБЫ ИМЕТЬ

http://kozmic.net/2012/10/23/ioc-container-solves-a-problem-you-might-not-have-but-its-a-nice-problem-to-have/


вам не нужна структура для достижения инъекций зависимостей. Вы также можете сделать это с помощью основных концепций java. http://en.wikipedia.org/wiki/Dependency_injection#Code_illustration_using_Java


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

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

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

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


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