Аргументы против инверсии управляющих контейнеров

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

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

То, что я не получаю:

EDIT: (Лучшая фраза) Зачем использовать непрозрачный контейнер, который не является идиоматическим для языка, когда вы можете "подключить" приложение в (imho) гораздо более ясным способом, если компоненты были правильно спроектированы (с использованием шаблонов IoC, развязка)? Как этот "управляемый код" получает нетривиальную функциональность? (Я слышал некоторые упоминания об управлении жизненным циклом, но я не всегда понимаю, как это лучше/быстрее, чем делать сами).

ОРИГИНАЛ: Зачем переходить ко всем длинам хранения компонентов в контейнере, "подключая их" способами, которые не являются идиоматическими для языка, используя вещи, эквивалентные "меткам goto", когда вы вызываете компоненты по имени, а затем теряете многие из преимуществ безопасности статически типизированного языка путем ручного литья, когда вы получите эквивалентную функциональность, не делая этого, и вместо этого используете все классные функции абстракции, данные современными языками OO, например программирование на интерфейс? Я имею в виду, что части, которые действительно должны использовать компонент под рукой, должны знать, что они его используют в любом случае, и здесь вы будете делать "проводку", используя самый естественный, идиоматический способ программирования!

Ответ 1

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

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

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

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

Кстати, большинство контейнеров, которые я знаю, не используют имена для проводов - они используют Авто-проводку, которая объединяет статическую информацию от Constructor Injection с конфигурацией контейнеров сопоставлений от интерфейсов к конкретным классам, Короче говоря, контейнеры изначально понимают эти шаблоны.

Контейнер DI не требуется для DI - он просто полезен.


Более подробное лечение можно найти в статье Когда использовать контейнер DI.

Ответ 2

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

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

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

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

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

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

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

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

Ответ 3

Прежде всего, что такое МОК? Это означает, что ответственность за создание зависимого объекта отнимается от основного объекта и делегируется сторонней структуре. Я всегда использую spring как свою структуру IOC, и это приносит массу пользы для таблицы.

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

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

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

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

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