Недавно я работал над проектом Python, в котором мы интенсивно делали внедрение зависимостей (потому что мы должны сделать так, чтобы приложение было тестируемым), но мы не использовали какую-либо инфраструктуру. Временами было немного утомительно связывать все зависимости вручную, но в целом все работало замечательно.
Когда объект должен был быть создан в нескольких местах, у нас просто была функция (иногда метод класса этого объекта) для создания производственного экземпляра. Эта функция вызывается всякий раз, когда нам был нужен этот объект.
В тестах мы делали одно и то же: если несколько раз нам нужно было создать «тестовую версию» объекта (т. Е. Реальный экземпляр, поддерживаемый фиктивными объектами), у нас была функция для создания этой «тестовой версии». класса, который будет использоваться в тестах.
Как я уже сказал, в целом это работало нормально.
Сейчас я вхожу в новый проект Java, и мы решаем, использовать ли DI-фреймворк или делать DI вручную (как в предыдущем проекте).
Пожалуйста, объясните, как работа с DI-фреймворком будет отличаться, и каким образом она лучше или хуже, чем ручная «ванильная» инъекция зависимостей.
Изменить: Я также хотел бы добавить к вопросу: вы были свидетелем какого-либо проекта «профессионального уровня», который делает DI вручную, без рамки?
источник
Ответы:
Ключом DI-контейнеров является абстракция . Контейнеры DI отражают эту заботу о дизайне. Как вы можете догадаться, это оказывает заметное влияние на код, часто переводимый на меньшее количество конструкторов, сеттеров, фабрик и сборщиков. Как следствие, они делают ваш код более свободным (благодаря основному IoC ), более чистым и простым. Хотя и не без затрат.
Фон
Несколько лет назад такая абстракция требовала от нас написания различных конфигурационных файлов ( декларативное программирование ). Было фантастически видеть, что количество LOC значительно уменьшилось, но, хотя количество LOCS уменьшилось, число файлов конфигурации немного увеличилось. Для средних и небольших проектов это была не проблема, а для более крупных проектов "разбросанность кода", распределенная на 50% между кодом и XML-дескрипторами, стала проблемой ... Тем не менее, основной проблемой было сама парадигма. Это было довольно негибко, потому что дескрипторы оставляли мало места для настроек.
Парадигма постепенно сместилась в сторону соглашения по конфигурации , которое обменивает файлы конфигурации на аннотации или атрибуты. Это делает наш код чище и проще, в то же время предоставляя нам гибкость, которую XML не мог.
Вероятно, это самая существенная разница в том, как вы работали до сих пор. Меньше кода и больше магии.
С другой стороны ...
Соглашение о конфигурации делает абстракцию настолько тяжелой, что вы едва знаете, как она работает. Иногда кажется волшебным, и тут возникает еще один недостаток. Как только работа работает, многим разработчикам все равно, как она работает.
Это препятствие для тех, кто изучал DI с помощью DI-контейнеров. Они не до конца понимают актуальность шаблонов проектирования, передовой практики и принципов, которые были выделены контейнером. Я спросил разработчиков, знакомы ли они с DI и его преимуществами, и они ответили: - Да, я использовал Spring IoC -. (Что, черт возьми, это значит?!?)
Можно согласиться или нет с каждым из этих «недостатков». По моему скромному мнению, необходимо знать, что происходит в вашем проекте. В противном случае у нас нет полного понимания этого. Также стоит знать, для чего предназначен DI, и иметь представление о том, как его реализовать, это плюс для рассмотрения.
На положительной стороне ...
Одним словом производительность . Рамки позволяют нам сосредоточиться на том, что действительно имеет значение. Бизнес приложения.
Несмотря на отмеченные недостатки, для многих из нас (работа зависит от сроков и затрат), эти инструменты являются бесценным ресурсом. На мой взгляд, это должно быть нашим главным аргументом в пользу реализации DI-контейнеров. Производительность .
тестирование
Будь мы реализуем чистый DI или контейнеры, тестирование не будет проблемой. Наоборот. Одни и те же контейнеры часто предоставляют нам макеты для модульного тестирования из коробки. В течение многих лет я использовал свои тесты в качестве термометров. Они говорят мне, сильно ли я полагался на оборудование контейнеров. Я говорю это потому, что эти контейнеры могут инициализировать приватные атрибуты без сеттеров или конструкторов. Они могут вводить компоненты практически в любом месте!
Звучит заманчиво, верно? Быть осторожен! Не попадитесь в ловушку!
Предложения
Если вы решили внедрить контейнеры, я настоятельно рекомендую придерживаться передового опыта. Продолжайте реализовывать конструкторы, сеттеры и интерфейсы. Внедрите фреймворк / контейнер для их использования . Это упростит возможную миграцию на другую платформу или ее удаление. Это может значительно уменьшить зависимость от контейнера.
Относительно этой практики:
Когда использовать контейнеры DI
Мой ответ будет предвзятым из-за собственного опыта, в основном в пользу DI-контейнеров (как я уже говорил, я сильно сосредоточен на производительности, поэтому у меня никогда не было необходимости в чистом DI. Скорее наоборот).
Я думаю, что вы найдете интересную статью Марка Симана на эту тему, которая также отвечает на вопрос о том, как реализовать чистый DI .
Наконец, если бы мы говорили о Java, я бы сначала рассмотрел JSR330 - Dependency Injection .
Подведение итогов
Преимущества :
Недостатки :
Отличия от чистого DI :
источник
По моему опыту, структура внедрения зависимостей приводит к ряду проблем. Некоторые из проблем будут зависеть от структуры и инструментов. Там могут быть практики, которые смягчают проблемы. Но это проблемы, которые я затронул.
Неглобальные объекты
В некоторых случаях вы хотите внедрить глобальный объект: объект, который будет иметь только один экземпляр в запущенном приложении. Это может быть
MarkdownToHtmlRendererer
,DatabaseConnectionPool
, адрес для вашего апи сервера и т.д. Для этих случаев, рамка инъекции зависимости работать очень просто, вы просто добавить нужную зависимость в конструктор , и вы получите его.Но как насчет объектов, которые не являются глобальными? Что если вам нужен пользователь для текущего запроса? Что если вам нужна текущая активная транзакция базы данных? Что если вам нужен HTML-элемент, соответствующий div, который содержит форму, в которой находится текстовое поле, которое только что инициировало событие?
Решение, которое я видел в DI-фреймворках, - это иерархические инжекторы. Но это делает модель впрыска более сложной. Становится все труднее понять, что именно будет введено с какого уровня иерархии. Становится трудно определить, будет ли данный объект существовать для каждого приложения, для каждого пользователя, для каждого запроса и т. Д. Вы больше не можете просто добавлять свои зависимости и заставить его работать.
Библиотеки
Часто вам захочется иметь библиотеку кода для повторного использования. Это также будет включать инструкции о том, как создавать объекты из этого кода. Если вы используете инфраструктуру внедрения зависимостей, это, как правило, включает правила привязки. Если вы хотите использовать библиотеку, вы добавляете их обязательные правила к своим.
Однако могут возникнуть различные проблемы. Вы можете получить один и тот же класс, связанный с разными реализациями. Библиотека может ожидать определенных привязок. Библиотека может переопределить некоторые привязки по умолчанию, заставляя ваш код делать странные вещи. Ваш код может переопределить некоторую привязку по умолчанию, заставляя код библиотеки делать странные вещи.
Скрытая сложность
Когда вы пишете фабрики вручную, вы чувствуете, как зависимости приложения сочетаются друг с другом. Когда вы используете инфраструктуру внедрения зависимостей, вы этого не видите. Вместо этого объекты имеют тенденцию расти все больше и больше зависимостей, пока рано или поздно все не будет зависеть почти от всего.
Поддержка IDE
Ваша IDE, вероятно, не понимает структуру внедрения зависимостей. С помощью ручных фабрик вы можете найти всех вызывающих в конструкторе, чтобы выяснить все места, где может быть построен объект. Но он не найдет вызовы, генерируемые платформой DI.
Вывод
Что мы получаем от структуры внедрения зависимостей? Мы избегаем простого шаблона, необходимого для создания объектов. Я за устранение простодушного шаблона. Тем не менее, поддерживать простой шаблонный образец легко. Если мы собираемся заменить этот шаблон, мы должны настаивать на том, чтобы заменить его на что-то более простое. Из-за различных вопросов, которые я обсуждал выше, я не думаю, что фреймворки (по крайней мере, в том виде, в каком они стоят) соответствуют требованиям.
источник
Требуется ли Java + внедрение зависимостей = framework?
Нет.
Что мне покупает DI-фреймворк?
Построение объекта происходит на языке, который не является Java.
Если фабричные методы достаточно хороши для ваших нужд, вы можете сделать DI в java полностью на платформе бесплатно в 100% аутентичной pojo java. Если ваши потребности выходят за рамки этого, у вас есть другие варианты.
Шаблоны «Банды четырех» достигли многих замечательных результатов, но всегда имели свои недостатки, когда дело дошло до шаблонов творчества. Сама Java имеет здесь некоторые недостатки. Структуры DI действительно помогают с этой творческой слабостью больше, чем с настоящими инъекциями. Вы уже знаете, как это сделать без них. Насколько хорошо они вам сделают, зависит от того, сколько вам понадобится помощи при строительстве объекта.
Есть много творческих моделей, которые появились после «Банды четырех». Josh Bloch builder - замечательный взлом из-за отсутствия именованных параметров в java. Помимо этого, существуют конструкторы iDSL, которые предлагают большую гибкость. Но каждый из них представляет собой значительную работу и пример.
Фреймворки могут спасти вас от этой скуки. Они не лишены недостатков. Если вы не будете осторожны, вы можете оказаться в зависимости от структуры. Попробуйте поменять фреймворки после их использования и убедитесь сами. Даже если вы каким-то образом сохраняете общий характер xml или аннотаций, вы можете в конечном итоге поддерживать два основных языка, которые вы должны упоминать бок о бок при рекламе заданий на программирование.
Прежде чем вы станете слишком очарованы мощью DI-фреймворков, потратьте некоторое время и изучите другие фреймворки, которые предоставляют вам возможности, для которых вы могли бы подумать, что вам нужна DI-фреймворк. Многие расширились за пределы простого DI . Рассмотрим: Lombok , AspectJ и slf4J . Каждый из них перекрывается с некоторыми структурами DI, но каждый прекрасно работает сам по себе.
Если тестирование действительно является движущей силой этого, пожалуйста, посмотрите на: jUnit (на самом деле любой xUnit) и Mockito (на самом деле любой насмешливый), прежде чем вы начнете думать, что DI-фреймворк должен забрать вашу жизнь.
Вы можете делать то, что вам нравится, но для серьезной работы я предпочитаю хорошо заполненный ящик для инструментов, чем швейцарский армейский нож. Желание это было легче сделать эти строки, но некоторые из них работали, чтобы размыть их. Ничего плохого в этом нет, но это может усложнить этот выбор.
источник
Создание классов и присвоение им зависимостей - это часть процесса моделирования, забавные части. Это причина, почему большинство программистов любят программирование.
Решение о том, какая реализация будет использоваться и как создаются классы, - это то, что нужно будет каким-то образом реализовать, и это часть конфигурации приложения.
Написание текстов вручную - это утомительная работа. Структуры DI облегчают это, автоматически разрешая зависимости, которые могут быть разрешены, и требуя настройки для тех, которые не могут.
Использование современных IDE позволяет вам ориентироваться в методах и их использовании. Писать фабрики вручную довольно хорошо, потому что вы можете просто выделить конструктор класса, показать его использование, и он покажет вам все места, где и как создается конкретный класс.
Но даже с платформой DI вы можете иметь центральное место, например, конфигурацию построения графов объектов (с этого момента OGCC), и, если класс зависит от неоднозначных зависимостей, вы можете просто заглянуть в OGCC и посмотреть, как создается конкретный класс прямо там.
Вы можете возразить, что лично вы считаете, что возможность увидеть использование конкретного конструктора является большим плюсом. Я бы сказал, что то, что лучше для вас, не только субъективно, но в основном из личного опыта.
Если бы вы всегда работали над проектами, основанными на DI-фреймворках, вы бы действительно знали только об этом, а когда вы хотели увидеть конфигурацию класса, вы всегда смотрели бы на OGCC и даже не пытались бы увидеть, как используются конструкторы. потому что вы знаете, что зависимости все равно автоматически подключаются.
Естественно, DI-фреймворки нельзя использовать для всего, и время от времени вам придется писать один или два заводских метода. Очень распространенным случаем является построение зависимости во время итерации по коллекции, где каждая итерация предоставляет различный флаг, который должен создавать различную реализацию в общем случае общего интерфейса.
В конце концов, скорее всего, все сводится к тому, каковы ваши предпочтения и чем вы готовы пожертвовать (будь то написание фабрик или прозрачность).
Личный опыт (предупреждение: субъективное)
Говоря как разработчик PHP, хотя мне нравится идея DI-фреймворков, я использовал их только один раз. Это было, когда команда, с которой я работал, должна была развернуть приложение очень быстро, и мы не могли терять время на написание фабрик. Таким образом, мы просто взяли DI-фреймворк, настроили его, и это как-то сработало .
Для большинства проектов мне нравится, когда фабрики пишутся вручную, потому что мне не нравится черная магия, происходящая в рамках DI. Я был ответственным за моделирование класса и его зависимостей. Я один решил, почему дизайн выглядит так, как он есть. Поэтому я буду тем, кто правильно конструирует класс (даже если класс принимает жесткие зависимости, такие как конкретные классы вместо интерфейсов).
источник
Лично я не использую DI-контейнеры и не люблю их. У меня есть еще несколько причин для этого.
Обычно это статическая зависимость. Так что это просто сервисный локатор со всеми его недостатками. Тогда из-за характера статических зависимостей они скрыты. Вам не нужно иметь с ними дело, они как-то уже есть, и это опасно, потому что вы перестаете их контролировать.
Он нарушает принципы ООП , такие как сплоченность и метафора лего-кирпича Дэвида Веста .
Таким образом, создание всего объекта как можно ближе к точке входа в программу - это путь.
источник
Как вы уже заметили: в зависимости от языка, который вы используете, существуют разные точки зрения, различные взгляды решают, как справляться с подобными проблемами.
Что касается внедрения зависимостей, то это сводится к следующему: внедрение зависимостей, как говорит сам термин, не более, чем помещение зависимостей, в которых нуждается один объект, в этот объект - в отличие от другого: позволить самому объекту создавать экземпляры объектов, от которых он зависит. Вы расцепить создание объекта и потребление объекта на победу больше гибкости.
Если ваш дизайн хорошо продуман, у вас обычно мало классов с множеством зависимостей, поэтому подключение зависимостей - либо вручную, либо через структуру - должно быть относительно простым, а количество шаблонов для написания тестов должно быть легко управляемым.
Тем не менее, нет необходимости в DI-фреймворке.
Но почему вы хотите использовать фреймворк?
I) Избегайте шаблонного кода
Если ваш проект приобретает размеры, когда вы чувствуете боль от написания фабрик (и инстаций) снова и снова, вы должны использовать DI-каркас
II) Декларативный подход к программированию
Когда-то Java когда-то программировал на XML, сейчас это так: поливать Fairyust аннотациями, и происходит волшебство .
А если серьезно: я вижу явный плюс в этом. Вы четко выражаете намерение, говоря, что нужно, а не где его найти и как его построить.
ТЛ; др
DI-фреймворки не делают вашу кодовую базу волшебным, но она помогает сделать хорошо выглядящий код действительно четким . Используйте это, когда вы чувствуете, что вам это нужно. В определенный момент в вашем проекте это сэкономит ваше время и предотвратит тошноту.
источник
Да. Возможно, вам следует прочитать книгу Марка Симанна под названием «Инъекция зависимости». Он описывает некоторые хорошие практики. Исходя из того, что вы сказали, вы можете извлечь из этого пользу. Вы должны стремиться иметь один корень композиции или один корень композиции на единицу работы (например, веб-запрос). Мне нравится его способ мышления, что любой объект, который вы создаете, считается зависимостью (как и любой параметр метода / конструктора), поэтому вы должны быть осторожны с тем, где и как вы создаете объекты. Фреймворк поможет вам сохранить ясность этих концепций. Если вы прочитаете книгу Марка, вы найдете больше, чем просто ответ на свой вопрос.
источник
Да, при работе на Java я бы порекомендовал DI-фреймворк. Есть несколько причин для этого:
В Java есть несколько очень хороших пакетов DI, которые предлагают автоматическое внедрение зависимостей, а также, как правило, поставляются с дополнительными возможностями, которые труднее писать вручную, чем с простым DI (например, области видимости зависимостей, которые позволяют автоматическое соединение между областями путем генерации кода для поиска того, какая область должна быть в используйте d, чтобы найти правильную версию зависимости для этой области - так, например, объекты области приложения могут ссылаться на объекты области запроса).
Java-аннотации чрезвычайно полезны и обеспечивают отличный способ настройки зависимостей
конструкция java-объекта слишком многословна по сравнению с тем, к чему вы привыкли в python; использование здесь фреймворка экономит больше работы, чем на динамическом языке.
Фреймворки java DI могут делать полезные вещи, такие как автоматическое создание прокси и декораторов, которые больше работают в Java, чем в динамическом языке.
источник
Если ваш проект достаточно мал и у вас нет большого опыта работы с DI-Framework, я бы рекомендовал не использовать Framework и делать это вручную.
Причина: однажды я работал в проекте, в котором использовались две библиотеки, которые имели прямые зависимости от разных сред. у них обоих был специфичный для фреймворка код, такой как di-give-me-a-instance-of-XYZ. Насколько я знаю, у вас может быть только одна активная реализация di-framework одновременно.
с таким кодом вы можете принести больше вреда, чем пользы.
Если у вас больше опыта, вы, вероятно, абстрагируете ди-фреймворк с помощью интерфейса (фабрика или сервис-локатор), поэтому эта проблема больше не будет существовать, и фреймворк di принесет больше пользы, чем вред.
источник