Когда контейнеры для инъекций зависимостей становятся слишком большими, и что я могу сделать с этим?

Мы все знаем, почему Dependency Injection является удивительным, потому что он делает код менее связанным, легче тестировать и гораздо приятнее читать! И затем некоторые решили использовать контейнер для инъекций зависимостей, например, прыщ для PHP, чтобы помочь с принципом инверсии зависимостей в SOLID .

Поэтому, создавая свой DiC, используя прыщ, передавая его контроллеру и создавая все ваши новые объекты в закрытии, которые на самом деле создаются только тогда, когда разработчик вызывает $container['object'] , это здорово!

Но что происходит, когда у вас очень большой набор классов в вашем приложении ? Скажите 1000+, и вы хотите, чтобы они были доступны в контейнере?

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

На стороне разделения, как насчет:

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

С другой стороны, я знаю, что Symfony2 использует XML / YAML для конфигурации DiC, но на самом деле это не говорит о архитектурной стороне вещей, когда приложение содержит так много классов.

Что может сделать разработчик, если у них такая большая база кода?

Solutions Collecting From Web of "Когда контейнеры для инъекций зависимостей становятся слишком большими, и что я могу сделать с этим?"

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

Сначала немного сравнительного случая: если вы идете в магазин, чтобы купить пару штанов, вы используете DI в несколько мгновений. Например, вам не нужно указывать, какой размер вам нужен, люди, которые помогают вам иметь опыт, чтобы узнать. Вам нужно будет сказать, какой тип брюк вы хотите иметь, люди в магазине представят вам некоторые брюки в соответствии с вашими пожеланиями (или дайте знать, что это невозможно). Примером, который не является DI, является ответ на вопрос, из которого происходят все эти брюки. Для клиента, входящего в магазин, ответ на этот вопрос совершенно неактуальен. Это никогда не будет причиной входа в магазин, где нужна пара брюк определенного типа. Вы можете задавать неуточненные вопросы определенного типа, и вы получите точные ответы. Это ядро ​​ДИ. Как это делается не в вашей компании, ни в вашей заботе. Но вы ничего не можете просить. Например, вы не можете купить хлеб в магазине ткани. Вопросы должны быть связаны с конкретным объектом DI (интерфейс).

Pimple – ассоциативный массив. Это не DI-контейнер. Насколько вам известно, DI-контейнер вернет интерфейс, где DI-контейнер знает, какая реализация загружается. Позвольте мне привести вам пример, чтобы показать вам, где Pimple не является контейнером DI. Вы в магазине, вы выбрали пару штанов, и вы хотите их купить. Теперь вы заплатите. Как? Это снова DI. Для вас это не проблема, у вас есть несколько способов для транзакции, и вы ее выберете. Как система ответит вам неинтересно: вы ничего не скажете, просто получите свой кошелек и начните платить.

В Pimple вам нужно будет выбрать объект для использования. Клиент должен будет сказать: «Мне нужен объект платежа». В истинном контейнере DI вам просто нужно будет сдать деньги законным способом (поведение интерфейса), и на основе входных данных DI-контейнер узнает, какую реализацию выбрать (наличные деньги, кредитная карта, основная карта). Тебе не о чем беспокоиться. Если вам все равно придется беспокоиться об этом, зачем называть его инъекцией зависимостей? Pimple в своем лучшем случае является локатором сервисов, но для использования DI вам нужно будет использовать интерфейс. В противном случае нет ничего, что можно было бы скрыть, никакой зависимости от инъекции. 🙂

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

Попробуйте организовать свою кодовую базу. Каковы функции разных классов? Какие классы нужны DI? Я предлагаю вам использовать только DI для классов, которые выполняют функциональные требования. Когда вы вернетесь к делу магазина: все функции, с которыми персонал магазина напрямую общается с клиентом. Не для реализации, как идти к заднему концу, пытаясь найти еще пару брюк. Не для процесса открытия или закрытия магазина. Но да, как приветствовать клиента и спрашивать, какого типа брюки кто-то хочет иметь. В вашем приложении: существуют ли интерфейсы / классы, которые используются непосредственно для взаимодействия с посетителями приложения, и можете ли вы создать какой-то контракт, как описывать взаимодействие? Это конструкция этого DI-контейнера. Я бы не использовал DI повсюду. DI поставляется со снижением производительности и обслуживанием. Чем больше вы используете DI, тем больше у вас будет слоев, тем меньше вы узнаете, что происходит где. Используйте DI предпочтительно там, где это наиболее выгодно, и именно там, где наиболее вероятно, что реализация изменится, но вызывающий не будет знать, что интерфейс изменился, и не вызывает заинтересованность в таком изменении. Если вы примете это в качестве ориентира, то можете ли вы сделать различия, какие классы скрывать через DI-контейнер, а какие нет.

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

Действительно ли вы используете все 1000 классов в качестве зависимостей?

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

Кроме того, DIC, скорее всего, не будет управляться такими вещами, как Views и другие функциональные классы и код.

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

Я хотел бы сейчас ответить на этот вопрос.

Контейнеры для инъекций зависимостей не содержат всех объектов в вашем приложении. Тот факт, что они обозначены контейнерами, является основной проблемой здесь. «DiC», как « Символ симфонии» , не являются инжекторами зависимости . Они являются прославленными ассоциативными массивами, используемыми для хранения объектов, и в первую очередь защищают анти-шаблон.

См. $this->get('something') в вашем коде, например, в Symfony? Это локатор сервисов. Используйте это, и вы скрываете зависимости классов и создаете лжеца вашего API объекта. Это включает в себя ваши контроллеры!

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

Контейнеры для инъекций зависимостей должны быть переименованы в инжекторы , потому что это то, что они должны делать, – инъекционные зависимости для вас. Вы не должны вытаскивать из них предметы, когда вам нужно. Как вы собираетесь использовать DI и инверсию управления, если вы просто вытаскиваете их, когда вам это нужно, используя локатор сервисов?

В реальной жизни вы не строили бы дом, транспортируя весь хозяйственный магазин (надеюсь) на строительную площадку, чтобы вы могли получить доступ к любым частям, в которых вы нуждаетесь. Вместо этого бригадир ( __construct() ) запрашивает конкретные детали, которые будут необходимы ( Door и Window ), и идет о их приобретении. Ваши объекты должны функционировать одинаково; они должны запрашивать только конкретные зависимости, необходимые для выполнения своих задач. Предоставление доступа к House во всем хозяйственном магазине в лучшем случае является плохим стилем ООП и, в худшем случае, ночным кошмаром для новинок. rdlowrey – auryn

Вы можете использовать рефлексию для чтения требований к объектам, а затем создавать экземпляры и вводить зависимости автоматически. Аурин – фантастический инжектор для этого. Вы настроили его в своем бутстрапе и контроллере, а затем вы можете написать SOLID-код для автоматической установки на основе типов объектов по всей вашей кодовой базе. Любые абстрактные классы / интерфейсы? Нет проблем, вы, как alias прописываете их либо непосредственно в коде, либо читаете их из файла конфигурации (предпочтительнее) и сглаживаете их в цикле таким образом. Использование файла конфигурации означает, что у вас может быть политический полиморфизм на основе конфигурации – возможность отключить одну конкретную реализацию другим путем простого создания нового объекта и изменения одной строки в файле конфигурации является фантастическим.

В заключение, контейнеры для инъекций зависимостей никогда не становятся «слишком большими», если вы не ошибаетесь и используете их как локатор сервисов или прославленный ассоциативный массив объектов! Вы не засунули все свои объекты там, чтобы вытащить их, когда они вам понадобятся. Меня не волнует, если они ленивы. Используйте фактический инжектор. И даже не упоминайте Laravel или «фасады».