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


598

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

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

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

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


6
Хороший вопрос. Я отвечаю на это в комментариях, поскольку я очень новичок в идее МОК. Похоже, идея подключи и играй компонентов и слабой связи. Необходимость использования какого-либо другого компонента вместо текущего зависит от необходимости. Использование МОК заключается в подготовке кода к такому изменению, если оно возникает в ИМО.
Шахкалпеш

3
@shahkalpesh, но я могу добиться слабой связи с простым DI. Тем не менее, я вижу вашу точку зрения в случае, если я использую файл конфигурации. Однако я не уверен, что мне нравится использовать файл конфигурации. Файлы конфигурации очень многословны, возникают трудности с рефакторингом и переключением между несколькими файлами.
Вадим

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

4
@Kevin, на самом деле мы используем IoC. Было не так сложно, как думали, рассказать всем об IoC.
Вадим

21
Я понятия не имею, почему модераторы закрывают массовые и полезные вопросы. Возможно, в этом случае, Уилл не понимает вопрос, или он не понимает, для чего люди используют stackexchange? Если он «не подходит для формата вопросов и ответов stackexchange», то, возможно, подумайте, что ваша политика неверна, а не все эти сотни полезных вопросов с сотнями голосов «за» и «полезных».
NickG

Ответы:


441

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

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:

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

28
К сожалению, вы забыли сделать все свойства виртуальными, и это не сработало. Тебе тоже нужно было потратить время на отладку кражи у своего клиента? (Извините, если вы не используете DynamicProxy.: P)
Thom

17
Вы жертвуете БОЛЬШОЙ ясностью, используя оболочку INotifyPropertyChanged. Если для начала написания этой сантехники вам понадобится больше нескольких минут, значит, вы не в том направлении. Меньше не больше, когда речь идет о многословности вашего кода!
выдержал

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

31
Понижено, потому что вы путаете шаблон внедрения зависимостей и шаблон локатора служб. Первый предназначен для использования вместо второго. Например, объект (или даже вся система) никогда не должен вызывать Resolve (...) для получения экземпляра IShippingService. Объекты, которым требуется IShippingService, должны получить ссылку на экземпляр, который они должны использовать при создании, и некоторый более высокий уровень отвечает за соединение двух объектов вместе.
Nat

30
Что бы это ни было (IoC, DynamicProxy, AOP или черная магия ), может кто-нибудь связать меня с конкретной статьей / конкретной документацией в рамках, которая предоставляет больше деталей для достижения этого?
fostandy

138

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

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

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

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

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


81
Я думаю, что вы немного перепутали свои факты, Джоэл. Сначала пришли IoC и контейнеры, а затем Мартина, а не наоборот.
Гленн Блок

55
Eesh большинство контейнеров позволит вам начать очень быстро. С помощью autofac, структурной карты, unity, ninject и т. Д. Контейнер будет работать примерно через 5 минут. Да, у них есть расширенные функции, но вам не нужны эти, чтобы оторваться от земли.
Гленн Блок

58
Джоэл, я привык думать так же, как ты. Затем я буквально и серьезно потратил пять минут, чтобы понять, как запустить самую простую установку, и был поражен правилом 80/20 в действии. Это делает код намного чище, особенно в простых случаях.
Джастин Бозонье

55
Я программирую менее двух лет, и я прочитал вики Ninject и получил ее. С тех пор я использую DI во многих местах. Ты это видел? Менее двух лет и читал несколько страниц в вики. Я не волшебник или что-то еще. Я не считаю себя гением, но мне было легко понять. Я не могу представить кого-то, кто действительно понимает, что ОО не в состоянии понять это. Кроме того, какие 200-300 страниц руководства вы имеете в виду? Я никогда не видел их. Все контейнеры IoC, которые я использовал, довольно короткие и точные.
JR Garcia

35
+1 хорошо написано и продумано. Я думаю, что многие люди не осознают, что добавление фреймворка или чего-то подобного для «волшебной» обработки чего-либо автоматически добавляет сложности и ограничений системе. Иногда это того стоит, но часто что-то упускается из виду, и энтропия высвобождается в код, пытаясь решить проблему, вызванную ограничениями. Это может сэкономить время заранее, но люди должны понимать, что это может стоить 100 раз, пытаясь решить какую-то непонятную проблему, только горстка людей действительно имеет достаточно знаний для решения.
kemiller2002

37

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

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

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

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

Марк Сееманн (Mark Seemann) пишет об этом в блоге: когда использовать DI-контейнер


Очень хороший ответ. Единственное, на что я бы поспорил, это то, может ли ручной впрыск КОГДА-ЛИБО считаться менее сложным или нет рутины.
Wekempf

+1. Один из лучших ответов здесь. Спасибо также за ссылку на статью в блоге.
stakx - больше не участвует

1
+1 за сбалансированную точку зрения. Контейнеры IoC не всегда хороши, и они не всегда плохи. Если вы не видите необходимости, не используйте ее. Просто знайте, что инструмент есть, если вы видите необходимость.
Фил

32

На мой взгляд, преимуществом 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, в отличие от более сложных файлов конфигурации, 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.
   }
}

11
Бен, то, о чем ты говоришь, это на самом деле расположение службы, а не инъекция зависимости - есть разница. Я всегда предпочитаю первое перед последним. stevenharman.net/blog/archive/2009/09/25/…
stevenharman

23
Вместо того, чтобы делать IoC.Resolve <T> в вашем конструкторе по умолчанию, вы должны использовать способность контейнера IOC для создания объекта для вас. Избавьтесь от этого пустого конструктора и позвольте контейнеру IOC создать объект для вас. var Presenter = IoC.Resolve <CustomerPresenter> (); вуаля! все зависимости уже подключены.
Бен Шейрман

12
Недостатком централизации такого рода конфигурации является де-контекстуализация знаний. Джоэл указывает на эту проблему, говоря: «Код становится, честно говоря, намного труднее читать».
Скотт Беллвер

6
@swareware, какие знания? Интерфейс, взятый как первичная зависимость, служит контрактом и должен быть достаточным, чтобы передать его цель и поведение, не так ли? Я полагаю, вы, возможно, имеете в виду знания, полученные в результате реализации контракта, и в этом случае вам нужно будет отследить соответствующую конфигурацию? Я полагаюсь на компьютер (R # в VS, IntelliJ и т. Д.) Для этого, поскольку они хороши в навигации по узлам и краям графа; Я резервирую свой мозговой стек для контекста рабочей площадки, на которой я сейчас сосредоточен.
Стивенхарман

7
Стив, я знаю текст .NET и гулял по нему много лет. У меня также есть близкое знакомство с другими способами и формами, и я понимаю, что есть реальные компромиссы. Я знаю, как и когда делать эти компромиссы, но разнообразие в моей трудовой жизни, по крайней мере, позволяет мне ощутимо понять точку зрения Джоэла. Вместо того, чтобы рассказывать мне об инструментах и ​​приемах, которыми я пользуюсь дольше, чем большинство монокультурщиков .NET, расскажите мне кое-что, чего я не знаю. Я резервирую свой мозг для разнообразного, критического мышления, а не стазиса и ортодоксии alt.net.
Скотт Беллвер

32

Контейнеры 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.
}

Но класс IoC вызывается с использованием анти-паттерна локатора службы. Нужно ли передавать _container с помощью конструктора DI вместо вызова статического T Resolve <T> ()?
Майкл Фрейдгейм

@bendewey Ваш пример подразумевает, что он автоматически передает аргументы конструкторам для нового CustomerService и нового CustomerRepository. Это как это работает? Что делать, если у вас есть и другие дополнительные параметры?
Brain2000

28

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

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

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

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

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


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

Мне нравится AspectJ. Это не Java, но и не XML. Это своего рода IS Java, в том , что он чувствует и выглядит как Java, будучи в ее продолжением. Я бы предпочел скрыть свои аспекты от моих POJO и в моей бизнес-логике. Я почти хотел бы также сохранить свои IoC. ИМХО, в связке инструментов слишком много XML. Если мне нужны конфигурационные файлы, пусть они будут скриптами BeanShell. / Java, примените ваши рабочие .Net здесь ->.
Крис К

2
Я, конечно, понимаю ваш ответ, но я думаю, что большая часть проблемы заключается в том, что многие инфраструктуры IoC (и, действительно, многие другие структуры также для других вещей) описаны и задокументированы для тех, кто уже хорошо знаком с необходимые понятия и терминология. Я узнаю о многом из этого, только что унаследовав код другого программиста. Я изо всех сил пытаюсь понять, почему код использует IoC, когда «графы объектов» довольно малы и нет реального покрытия кода тестами. Как программист SQL, вы, вероятно, лучше оцените использование IoC с ORM.
Кенни Эвитт

1
@KennyEvitt, хорошая идея, было бы преждевременным использовать IoC-фреймворк, если у вас есть простое приложение и даже не удосужились получить хорошее тестовое покрытие.
Билл Карвин

27

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

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

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

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

23

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

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

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

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

Cons

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

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


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

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

17

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

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

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

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

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

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

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

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


16

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

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

Я думаю, что когда автор упомянул IoC, подразумевалась структура контейнера IoC. Спасибо за оригинальную рецензию, это помогает убедить меня остаться без рамки. Так что, если он настраивается в тестах и ​​в рабочем коде, в чем преимущество «настраиваемого» фреймворка?
Хагги

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

14

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

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

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

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


2
Это огромный плюс. Не могу поверить, что никто не упомянул об этом ранее. Без необходимости передавать или хранить временные объекты делает код намного чище.
Finglas

1
Глобальные объекты @qble прекрасно работают ... если вы хотите, чтобы ваша кодовая база была тесно связанным, не поддающимся тестированию кошмаром, который со временем будет все труднее модифицировать. Удачи с этим.
Джеффри Кэмерон

2
@JeffreyCameron IoC Container - это глобальный объект. Это не удаляет ваши глобальные зависимости.
Qble

3
Правда, однако контейнер IoC прозрачен для остальной части приложения. Вы вызываете его один раз при запуске, чтобы получить экземпляр, а затем вы видите его последним. С глобальными объектами ваш код изобилует жесткими зависимостями
Джеффри Кэмерон

1
@qble использует фабрики для создания переходных процессов на лету. Заводы являются одноразовыми услугами. Если временным экземплярам нужно что-то из контейнера IOC, подключите их к фабрике и заставьте фабрику передать зависимость в временный экземпляр.
Джеффри Кэмерон

14

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

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

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

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

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-фреймворки, очень умны - я просто считаю, что они не знают математику.


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

Вы отбрасываете безопасность типов, так как не знаете, связан ли тип в IoC-контейнере (типа IoC <IBar> нет IBar, это на самом деле IO IBar) .. И да, в примере с Haskell Я не могу получить нулевую ссылку.
Финнссон

Безопасность типов на самом деле не связана с подключением объекта, по крайней мере, не в Java / C #, это просто объект правильного типа. И ConcreteClass myClass = null по-прежнему имеет правильный тип. Если вы хотите принудительно установить не ноль, тогда вступают в силу контракты кода.
Удар

2
Я в какой-то степени согласен с вашим 1-м пунктом, я хотел бы, чтобы какое-то объяснение 2-го и 3-го никогда не было для меня проблемой. Ваш пример C # использует контейнер IoC в качестве локатора службы, распространенная ошибка. И сравнивая C # против Haskell = яблоки против апельсинов.
Маурисио Шеффер

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

10

Я обнаружил, что правильная реализация 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, обратите внимание, что:

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

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


9

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

Беда в том, что вы должны где-то строить объекты. Фабричный шаблон - это один из способов снять связь с вашими POXO (Plain Old, «вставьте сюда свой язык OO», «Объекты»). Если вы и ваши коллеги пишете такой код, то контейнер IoC - это следующее «поэтапное улучшение», которое вы можете внести в свою кодовую базу. Это вытеснит весь этот мерзкий шаблонный код фабрики из ваших чистых объектов и бизнес-логики. Они получат это и полюбят это. Черт возьми, поговори с компанией о том, почему ты любишь это, и возбуди всех.

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


8

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


7

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

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

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


6

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


3
«но я инженер, работающий над интернет-технологиями в 21-м веке, поэтому я к этому привык» -> +1
user96403

5

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

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

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


1
почему мы продолжаем успокаивать наименьший общий знаменатель, а не стремимся их подтянуть?
Стивенхарман

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

4

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

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


2

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

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

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


Новая инфраструктура MEF допускает именно это для .NET, и я должен сказать, что код чище и проще для понимания, значительно облегчает понимание концепции DI.
terjetyl

4
@TT: MEF не является контейнером внедрения зависимостей и не имеет ничего общего с AOP.
Маурисио Шеффер

2

Так прошло уже почти 3 года, а?

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

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

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

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

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

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


1

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

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

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

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

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

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

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


Даже для небольших проектов размещение конфигурации в XML делает ее более чистой и модульной - и впервые делает код многократно используемым (так как он больше не загрязнен клеем). Для правильных программных продуктов вы можете настроить или заменить, например, ShippingCostCalculatorили ProductUIPresentation, или любую другую стратегию / реализацию, в пределах той же самой базы кода , развернув только один измененный файл XML.
Томас W

Если это будет реализовано как общая практика, я думаю, вы добавите больше сложности, а не меньше. Когда вам нужно что-то поменять во время выполнения, они великолепны, но зачем добавлять запутывание? Зачем держать накладные расходы на дополнительные интерфейсы для вещей, которые никогда не будут переключаться? Я не говорю, что не используйте IoC для тестирования. Во что бы то ни стало, используйте свойство или даже инъекцию зависимости конструктора. Вам нужно использовать интерфейс? Почему бы не создать подкласс для ваших тестовых объектов? Разве это не будет чище?
Фред

Вам вообще не нужны интерфейсы для настройки XML / IoC. Я обнаружил большие улучшения в ясности в небольшом проекте с ~ 8 страницами и 5 или 6 сервисами. Там меньше запутывания, так как сервисы и контроллеры больше не нуждаются в склеивании кода.
Томас W

1

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


1

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

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


1

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

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

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

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

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

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

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

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

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


0

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

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




0

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

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

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

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


-8

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


1
можешь привести пример? Чем отличается DI в ASP.NET по сравнению с любым другим типом приложения?
tofi9
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.