#inversion-of-control #ioc-container
#инверсия управления #ioc-контейнер
Вопрос:
Кажется, что все движутся в сторону контейнеров IoC. Я пытался «грокнуть» это некоторое время, и как бы сильно я не хотел быть единственным водителем, который поедет не в ту сторону на шоссе, для меня это все еще не проходит проверку на здравый смысл. Позвольте мне объяснить, и, пожалуйста, исправьте / просветите меня, если мои аргументы ошибочны:
Мое понимание: предполагается, что контейнеры IoC облегчают вашу жизнь при объединении различных компонентов. Это делается либо с помощью а) внедрения конструктора, б) внедрения установщика и в) внедрения интерфейса. Затем они «подключаются» программно или в файле, который считывается контейнером. Компоненты затем вызываются по имени, а затем при необходимости преобразуются вручную.
Чего я не понимаю:
РЕДАКТИРОВАТЬ: (Лучшая формулировка) Зачем использовать непрозрачный контейнер, который не является идиоматичным для языка, когда вы можете «подключить» приложение (имхо) гораздо более понятным способом, если компоненты были правильно спроектированы (с использованием шаблонов IoC, слабой связи)? Как этот «управляемый код» приобретает нетривиальную функциональность? (Я слышал несколько упоминаний об управлении жизненным циклом, но я не обязательно понимаю, чем это лучше / быстрее, чем «сделай сам».)
ОРИГИНАЛ: Зачем так долго хранить компоненты в контейнере, «подключать их» способами, которые не являются идиоматичными для языка, используя вещи, эквивалентные «goto labels», когда вы вызываете компоненты по имени, а затем теряете многие преимущества безопасности статически типизированного языка путем ручного приведения, когда вы получили бы эквивалентную функциональность, не делая этого, а вместо этого используя все замечательные возможности абстракции, предоставляемые современными языками OO, например, программирование в интерфейсе? Я имею в виду, что части, которым на самом деле нужно использовать имеющийся компонент, должны знать, что они используют его в любом случае, и здесь вы будете выполнять «подключение», используя самый естественный, идиоматический способ — программирование!
Ответ №1:
Конечно, есть люди, которые думают, что контейнеры DI не приносят никакой пользы, и вопрос справедлив. Если вы посмотрите на это исключительно с точки зрения композиции объекта, преимущество контейнера может показаться незначительным. Любая третья сторона может подключать слабо связанные компоненты.
Однако, как только вы выйдете за рамки игрушечных сценариев, вы должны понять, что третья сторона, которая соединяет сотрудников, должна взять на себя нечто большее, чем просто ответственность за композицию. Также могут возникнуть проблемы с выводом из эксплуатации для предотвращения утечек ресурсов. Поскольку композитор является единственной стороной, которая знает, был ли данный экземпляр общим или частным, он также должен взять на себя роль по управлению временем жизни.
Когда вы начинаете комбинировать различные области экземпляра, используя комбинацию общих и частных служб и, возможно, даже привязывая некоторые службы к определенному контексту (например, веб-запросу), все становится сложным. Конечно, можно написать весь этот код с помощью DI для бедных людей, но это не добавляет никакой ценности для бизнеса — это чистая инфраструктура.
Такой инфраструктурный код представляет собой универсальный поддомен, поэтому вполне естественно создать повторно используемую библиотеку для решения таких проблем. Это именно то, что представляет собой контейнер DI.
Кстати, большинство контейнеров, которые я знаю, не используют имена для подключения самих себя — они используют автоматическое подключение, которое объединяет статическую информацию из внедрения конструктора с конфигурацией контейнера сопоставлений интерфейсов с конкретными классами. Короче говоря, контейнеры изначально понимают эти шаблоны.
Контейнер DI не требуется для DI — это просто чертовски полезно.
Более подробную трактовку можно найти в статье Когда использовать контейнер DI.
Комментарии:
1. Спасибо за ваш ответ. Отметьте. Хорошо, я согласен, что общая функциональность должна быть абстрагирована и преобразована в повторно используемый компонент. И да, некоторые контейнеры действительно облегчают вашу жизнь (наиболее примечательные для меня: серверы приложений. Из вашего ответа они кажутся достаточно похожими на контейнеры IoC). Я не слышал об автоматическом подключении (возможно, потому, что этого не было в руководствах и статьях для начинающих …) Возможно, мне нужно узнать о них больше, но до тех пор я буду настроен скептически 🙂
2. @vivri: Какая у вас платформа? .NET? Java? Что-то еще?
3. В настоящее время я живу в экосистеме Java, и мы используем Spring там, где я работаю, но (а) очевидно, мы не используем его в полной мере (мы используем EJB, которые, по-видимому, устарели к Spring), и (б) я еще не сталкивался ни с чем, что требует его использования.
4. ОК. Я в основном знаком с . СЕТЧАТЫЕ контейнеры. На разных платформах есть различия, но Spring. NET поддерживает автоматическое подключение, хотя и не в такой степени, как другие контейнеры .NET.
5. Я предполагаю, что различия связаны с. Поддержка NET для лямбд.
Ответ №2:
Я уверен, что по этому вопросу можно многое сказать, и, надеюсь, я отредактирую этот ответ, чтобы добавить больше позже (и, надеюсь, больше людей добавят больше ответов и инсайтов), но всего пара кратких замечаний к вашему сообщению…
Использование контейнера IoC — это подмножество инверсии управления, а не все. Вы можете использовать инверсию элемента управления в качестве конструктивной конструкции, не полагаясь на фреймворк контейнера IoC. В самом простом виде инверсия элемента управления может быть сформулирована в этом контексте как «поставка, не создание экземпляра». До тех пор, пока ваши объекты внутренне не зависят от реализаций других объектов и вместо этого требуют, чтобы им предоставлялись экземпляры реализаций, вы используете инверсию управления. Даже если вы не используете фреймворк контейнера IoC.
К вашей точке зрения на программирование в интерфейсе… Я не уверен, каков был ваш опыт работы с контейнерами IoC (мой личный фаворит — StructureMap), но вы определенно программируете для интерфейса с IoC. Вся идея, по крайней мере в том, как я ее использовал, заключается в том, что вы отделяете свои интерфейсы (ваши типы) от своих реализаций (ваших внедренных классов). Код, который полагается на интерфейсы, запрограммирован только для них, и реализации этих интерфейсов вводятся при необходимости.
Например, у вас может быть IFooRepository
который возвращает из хранилища данных экземпляры типа Foo
. Весь ваш код, которому нужны эти экземпляры, получает их из предоставленного объекта типа IFooRepository
. В другом месте вы создаете реализацию FooRepository
и настраиваете свой IoC так, чтобы он предоставлялся везде, где IFooRepository
требуется. Эта реализация может получать их из базы данных, из XML-файла, из внешней службы и т.д. Не имеет значения, где. Этот элемент управления был инвертирован. Вашему коду, который использует объекты типа Foo
, все равно, откуда они берутся.
Очевидным преимуществом является то, что вы можете поменять эту реализацию в любое время, когда захотите. Вы можете заменить его тестовой версией, изменить версии на основе среды и т.д. Но имейте в виду, что вам также не нужно иметь такое соотношение интерфейсов к реализациям 1 к 1 в любой момент времени.
Например, я однажды использовал инструмент генерации кода на предыдущей работе, который выдавал тонны и тонны кода DAL в один класс. Разбить его на части было бы непросто, но что было не так уж сложно, так это настроить его так, чтобы все это отображалось в конкретных именах методов / свойств. Итак, я написал кучу интерфейсов для своих репозиториев и сгенерировал этот единственный класс, который реализовал все из них. Для этого сгенерированного класса это было некрасиво. Но остальной части моего приложения было все равно, потому что оно рассматривало каждый интерфейс как свой собственный тип. Контейнер IoC просто предоставил этот же класс для каждого из них.
Мы смогли быстро приступить к работе с этим, и никто не ждал разработки DAL. Пока мы продолжали работать над кодом домена, в котором использовались интерфейсы, младшему разработчику было поручено создать лучшие реализации. Эти реализации были позже заменены, все было хорошо.
Как я упоминал ранее, все это может быть выполнено без контейнерной платформы IoC. На самом деле важен сам шаблон.
Комментарии:
1. Спасибо за ваш ответ, Дэвид! Я согласен, что использование шаблона IoC важно и создает гораздо более совершенные API. Моя «проблема» заключалась в использовании фреймворков и контейнеров — я просто не вижу смысла хранить экземпляры объектов и «подключать» ваше приложение к тому, что составляет почти виртуальную машину, которая непрозрачна для вас.
Ответ №3:
Прежде всего, что такое IOC? Это означает, что ответственность за создание зависимого объекта снимается с основного объекта и делегируется сторонней платформе. Я всегда использую spring в качестве своей платформы IOC, и это приносит массу пользы таблице.
-
Способствует кодированию интерфейса и развязке — Ключевым преимуществом является то, что IOC продвигает и упрощает развязку. Вы всегда можете внедрить интерфейс в свой основной объект, а затем использовать методы интерфейса для выполнения задач. Главному объекту не обязательно знать, какой зависимый объект назначен интерфейсу. Если вы хотите использовать другой класс в качестве зависимости, все, что вам нужно, это поменять старый класс на новый в файле конфигурации без изменения одной строки кода. Теперь вы можете утверждать, что это может быть сделано в коде с использованием различных шаблонов проектирования интерфейса. Но фреймворк IOC совершает свою прогулку по парку. Таким образом, даже будучи новичком, вы становитесь экспертом в использовании различных шаблонов проектирования интерфейса, таких как bridge, factory и т.д.
-
Чистый код — Поскольку большая часть операций создания объекта и жизненного цикла объекта делегируется контейнеру IOC, который вы сохранили от написания повторяющегося кода broiler point. Таким образом, у вас есть более чистый, уменьшенный и понятный код.
-
Модульное тестирование — IOC упрощает модульное тестирование. Поскольку у вас остается несвязанный код, вы можете легко протестировать несвязанный код изолированно. Также вы можете легко внедрить зависимости в свои тестовые случаи и посмотреть, как взаимодействуют разные компоненты.
-
Конфигураторы свойств — Почти у всех приложений есть некоторый файл свойств, в котором они хранят статические свойства, специфичные для приложения. Теперь для доступа к этим свойствам разработчикам необходимо написать оболочки, которые будут считывать и анализировать файл свойств и сохранять свойства в формате, доступном приложению. Теперь все фреймворки IOC предоставляют способ введения статических свойств / значений в определенный класс. Итак, это снова становится прогулкой в парке.
Это некоторые из пунктов, о которых я могу подумать сразу, я уверен, что есть и другие.