Это инъекция зависимостей, и это плохая практика?

У меня небольшая структура, и я закодировал ее так. Я не уверен, что это называется инъекцией зависимости или нет. Я не знаю, похоже ли это на шаблон дизайна или нет. Я также не знаю и задаюсь вопросом, является ли передача $this переменной параметром плохой практики.

Посмотри на это; (Не рабочий пример, просто написал эти коды в браузере для объяснения.)

 /* This is engine model */ require_once('Database.class.php'); require_once('Image.class.php'); require_once('Misc.class.php'); require_once('BBCode.class.php'); class FrameWork_Engine_Model { public $database, $config, $misc, $bbcode, $controller, $image; function __construct($config) { $this->database = new Database($configParams); $this->image = new Image($this); $this->misc = new Misc($this); $this->bbcode = new BBCode($this); $this->controller = new Controller($this); //here I call Register controller depending on routing, in this case, register controller. } ... } 

  /* This is register controller */ class Register extends Base_Controller { /*I can access anything over Engine Model in my controllers */ $this->engine->database->query(); //I access database model $this->engine->bbcode->tag('you'); //I access bbcode model $this->engine->image->sanitizeUploadedFile(); //I access image model //etc. I can access others models like this. } 

В принципе, мои контроллеры могут получить доступ к любым моделям с помощью модели двигателя. Я полагаю, что dependency injection is all about injecting dependencies into controllers? Например, моему контроллеру регистров нужна модель базы данных, модель маршрутизации и шаблонная модель. Здесь у него есть все, от чего это зависит. Я ошибаюсь?

С учетом сказанного мои вопросы:

  1. Является ли это примером примера внедрения зависимостей? Если нет, то что это такое? У него есть имя в шаблонах дизайна?

  2. Если это ничего не связано с инъекцией зависимости, какие изменения нужно сделать, чтобы быть DI?

  3. Пропускает ли $this параметр на вновь созданных классах плохую практику? Если да, то почему?

Ps. Я знаю, что задавать 3 вопроса в теме – это не то, что нравится stackoverflow, но я не хочу копировать весь текст, чтобы спросить их.

Related of "Это инъекция зависимостей, и это плохая практика?"

Вы почти там.

Вопрос 1

Нет, я не вижу его в качестве допустимого примера инъекции зависимостей. Он немного напоминает локатор сервисов (потому что вы вводите весь контейнер в свои службы и используете его для «поиска» зависимых сервисов).

вопрос 2

Вы делаете небольшую путаницу между инъекцией зависимостей и контейнером для инъекций зависимости.

Во-первых, инъекция зависимостей означает нажатие зависимостей на объект во время выполнения, а не на создание / вытягивание.

Чтобы проиллюстрировать это:

 //hardcoded dependecies class BadService { public function __construct() { $this->dep1 = new ConcreteObject1(); $this->dep2 = new ConcreteObject2(); } } 

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

 //service locator pattern class AlmostGoodService { public function __construct(Container $container) { $this->dep1 = $container->getADep1(); $this->dep2 = $container->getADep2(); } } 

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

 //dependecy injection class GoodService { public function __construct($dep1, OptionalInterface $dep2) { $this->dep1 = $dep1; $this->dep2 = $dep2; } } 

Служба GoodService не связана с созданием ее конкретных зависимостей и может быть легко «подключена» во время выполнения с любыми зависимостями, которые реализуют «протокол» $dep1 или OptionalInterface для $dep2 (поэтому имя Inversion of Control – базовая концепция внедрения инъекций).

Компонент, который выполняет эту проводку, называется контейнером для инъекций зависимостей .

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

Я сказал, что вы почти там, но есть некоторые проблемы с вашей реализацией:

  • проводка должна быть ленивой (вы не хотите делать все, что работает в вашем конструкторе, так как ваше приложение будет значительно замедляться по мере его роста)
  • вы не должны передавать весь контейнер ( $this ) в качестве зависимости, потому что тогда вы отказываетесь от более слабого обращения управления , а именно к локатору службы . Вместо этого вы должны передавать конкретные зависимости вашим сервисным конструкторам

Вопрос 3

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

Реализация прототипа контейнера

Итак, основываясь на моих ответах выше, вот пересмотренная (далеко не идеальная) реализация:

 /* This is the revised engine model */ class FrameWork_Engine_Model { function __construct($config) { $this->config = $cofig; } public function database() { require_once('Database.class.php'); return new Database($this->config['configParams']); } public function bbcode() { require_once('BBCode.class.php'); return new BBCode($this->database()); } public function image() { require_once('Image.class.php'); $this->image = new Image($this->config['extensionName']); } .... public function register_controller($shared = true) { if ($shared && $this->register_controller) { return $this->register_controller; } return $this->register_controller = new Register_Controller($this->database(), $thus->image(), $this->bbcode()); } } 

Теперь, чтобы использовать ваши услуги:

 $container = new FrameWork_Engine_Model(); $container->register_controller()->doSomeAction() 

Что можно улучшить? Ваш контейнер должен:

  • предоставить способ обмена услугами, то есть инициализировать их только один раз
  • быть заблокированным – обеспечить способ блокировки после настройки
  • быть в состоянии «слиться» с другими контейнерами – таким образом ваше приложение будет действительно модульным
  • разрешить дополнительные зависимости
  • разрешать области
  • поддержка тегов

Готовые к использованию DI-контейнеры

Все они сопровождаются четкой документацией об инъекции зависимостей

  • Pimple – легкий вес DI 5.3.
  • Контейнер Symfony2 DI – PHP 5.3 имеет полный контейнер DI
  • Juice DI – Маленький контейнер PHP 5.2 DI
  1. Ваш FrameWork_Engine_Model – это реестр ( шаблон реестра ). Инъекция реестра как зависимость во всех объектах – это некорректная инъекция зависимостей. Технически это DI, но вы создаете зависимость от всего ко всему, а также отбираете гибкость, которую должен предложить DI.
  2. Если ваш FrameWork_Engine_Model предназначался для создания служб и управления их зависимостями, вы можете изменить его на Inversion of Control Container (типичный шаблон, связанный с DI)
  3. Нет, не в общем.

Я не буду спорить о вашем выборе имен классов и обязанностей ваших служб и контроллеров, так как я не думаю, что это в рамках этого вопроса. Просто замечание: похоже, что ваши контроллеры слишком много делают. Если вы заинтересованы в чистом коде, вы можете взглянуть на принцип единой ответственности и держать свои контроллеры «тонкими», перемещая бизнес-логику и запросы к базе данных на уровень обслуживания и выводить механизмы, такие как bbcode, в представления.

Итак, вернемся к вашему примеру и как изменить его на разумное использование Injection Dependency. Первичный контейнер IoC может выглядеть так:

 public function createRegisterController() { $controller = new RegisterController(); $controller->setImage($this->getImageService()); // ... return $controller; } public function getImageService() { if ($this->imageService === null) { $this->imageService = new Image(); // inject dependencies of Image here } return $this->imageService; } 

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