2010-03-24 5 views
9

Я начинаю новый проект и создаю базу для работы. Несколько вопросов поднялись, и я, вероятно, буду спрашивать здесь немало, надеюсь, что найду ответы.Доступ к контейнеру 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 внутри классов, например, в контроллерах, которым требуется доступ к различным моделям? Должен ли я называть его статически или передавать его по каждому классу, который потребует его? Я не думаю, что последняя идея возможна.

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

ответ

21

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

Dependency Injection основан на две части кода:.

  1. Строительство
  2. Execution

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

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

  • А зависит от B, и
  • B зависит от C.

Затем

  • С строится в первую очередь.
  • Затем B сконструирован с параметром C в качестве параметра.
  • Затем A строится с B как параметр.

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

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

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

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

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

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

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

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

Приложение: Немного больше о Поставщиков

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

Предположим, что B зависит от C.

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

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

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

+0

Проницательный, спасибо за объяснение. Я знаю о шаблоне Locator Service, и это не то, что я пытаюсь выполнить. Не могли бы вы немного рассказать об использовании провайдеров? – Andre

+0

Добавлено добавление. Дайте мне знать, если вам нужна дополнительная информация. –

+0

Большое спасибо за помощь. В основном мне нужно пройти по требуемым провайдерам для каждой многопоточной зависимости. Это в основном шаблоны Factory, но специфические для создания объектов для целей (могут быть различные классы, примером могут быть выходные драйверы) и вернуть их в зависимый класс. Похоже на увеличение сложности: D – Andre

2

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

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

2

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

+0

Цель состоит в том, чтобы избежать использования каких-либо сторонних библиотек, если это абсолютно необходимо (в основном из-за проблем с лицензированием). – Andre

+0

@andre: Symfony лицензируется по лицензии MIT. Прочтите, в основном говорится: «Делайте то, что хотите»: http://www.opensource.org/licenses/mit-license.php –

+2

Я создаю свою собственную лицензию и не хочу приписывать сторонние библиотеки или программное обеспечение (часть требований к проектам), поэтому мне нужно создать свой собственный контейнер. – Andre

Смежные вопросы