Доступ к контейнеру DI

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

Первый шаг - обрабатывать зависимости для объектов. Я решил пойти с шаблоном проектирования инъекций зависимостей, к которому я отношусь несколько новым, для обработки всего этого для приложения.

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

Как вы это делаете, не передавая массив зависимостей, используя call_user_func_array(), eval() или Reflection? Это то, что я ищу:

<?php

class DI
{
    public function getClass($classname)
    {
        if(!$this->pool[$classname]) {
            # Load dependencies
            $deps = $this->loadDependencies($classname);

            # Here is where the magic should happen
            $instance = new $classname($dep1, $dep2, $dep3);

            # Add to pool
            $this->pool[$classname] = $instance;

            return $instance;
        } else {
                return $this->pool[$classname];
        }
    }
}

Опять же, я хотел бы избежать самых дорогостоящих методов вызова класса. Любые другие предложения?

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

Спасибо всем.

Ответ 1

[Прежде чем начать, позвольте мне сказать, что я в основном программист на Java - всего лишь немного знаний PHP. Но я просто попытаюсь использовать самые важные понятия без специфики языка.]

Инъекция зависимостей основана на двух частях кода:

  • Строительство
  • Выполнение

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

Вся конструкция происходит - в строительной части. Он создает график объектов, необходимых для выполнения снизу вверх. Поэтому допустим, что он должен построить A:

  • A зависит от B и
  • B зависит от C.

Тогда

  • C построено первым.
  • Тогда B строится с C как параметром.
  • Тогда A строится с B как параметр.

Таким образом, C не нужно передавать как параметр конструктора в A. Этот небольшой пример недостаточно иллюстрирует, насколько это уменьшает количество объектов, которые должны быть переданы на довольно небольшое число.

Сам инжектор зависимостей не должен передаваться в часть выполнения. Это одна из основных ошибок, которые каждый (включая меня) пытается сделать, когда они впервые вступают в контакт с ДИ. Проблема в том, что это полностью размывает линии между строительством и исполнением. Другой способ сказать, что это нарушит Закон Деметры. Или в шаблоне говорят: он в конечном итоге "деградирует" шаблон "Зависимость впрыска" к шаблону "Локатор службы". Это спорно, если это действительно деградация, но в любом случае это, как правило, не очень хорошая идея, чтобы злоупотреблять Dependency Injector как локатор.

Поэтому всякий раз, когда вам нужно дать одному из ваших построенных объектов возможность создавать другие объекты во время выполнения, вместо передачи инжектора зависимостей, вы будете передавать только простые провайдеры (термин, используемый средой Java DI Guice). Это довольно простые классы, которые могут создавать только определенный объект. Они имеют сходство с factory.

Сначала попробуйте передать необходимые зависимости непосредственно конструктору.

Итак, подведем итог:

  • Создание объектов снизу вверх.
  • Выполнять только несколько зависимостей, необходимых для создания объекта.
  • Как только вы закончите, начните выполнение.
  • Во время выполнения вы можете получать новые созданные объекты с помощью Провайдеров.

Но не заходите слишком далеко: простые объекты могут быть созданы без провайдера: -)

И теперь все, что вам нужно сделать, это перевести этот материал в качественный код. Возможно, другие могут помочь вам с несколькими примерами PHP.

Добавление: Немного больше о Провайдерах

Как отмечалось выше, понятие "поставщик" (специализированный factory) является немного специфичным для платформы Java DI Guice. Эта структура может автоматически создавать Провайдера для любого типа объекта. Однако эта концепция, как правило, полезна для DI. Единственное различие заключается в том, что без помощи Guice или аналогичной структуры вам придется сами писать Провайдеры, но это довольно легко:

Скажем, B зависит от C.

  • Если B просто нужен один фиксированный экземпляр C, тогда вам не нужен провайдер - вы можете просто построить B с аргументом конструктора C.
  • Если B нужно создать больше экземпляров C во время выполнения, просто напишите класс CProvider с помощью метода get(), который может создать новый экземпляр C. Затем передайте экземпляр CProvider в конструктор B и сохраните Поставщик в поле экземпляра B. Теперь B может вызвать cProvider.get(), когда ему нужен новый экземпляр C.

Поставщики являются частью кода строительства, поэтому вам разрешено использовать new C(...)! С другой стороны, они не являются частью кода Execution, поэтому у вас не должно быть никакой логики выполнения.

CProvider можно, конечно, передать в несколько конструкторов. Вы также можете написать несколько версий CProvider1, CProvider2,... - где каждый может создавать разные версии объектов C с разными свойствами. Или вы просто создаете экземпляр CProvider несколько раз с разными аргументами.

Ответ 2

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

Существует существующий question вопрос о вариантах контейнера IOC для PHP.

Ответ 3

Похоже, вы пытаетесь перевернуть свой собственный контейнер инъекций зависимостей. Почему бы не использовать тот, который уже существует, например Symfony, Crafty или Sphicy?