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

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

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

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

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

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

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

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

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

Ответ 1

Теперь я хотел бы ответить на этот вопрос.

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

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

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

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

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

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

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

Ответ 2

Скажем, что все ваши классы будут вызваны через контейнер 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-контейнер, а какие нет.

Ответ 3

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

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

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

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

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