У меня небольшая структура, и я закодировал ее так. Я не уверен, что это называется инъекцией зависимости или нет. Я не знаю, похоже ли это на шаблон дизайна или нет. Я также не знаю и задаюсь вопросом, является ли передача $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?
Например, моему контроллеру регистров нужна модель базы данных, модель маршрутизации и шаблонная модель. Здесь у него есть все, от чего это зависит. Я ошибаюсь?
С учетом сказанного мои вопросы:
Является ли это примером примера внедрения зависимостей? Если нет, то что это такое? У него есть имя в шаблонах дизайна?
Если это ничего не связано с инъекцией зависимости, какие изменения нужно сделать, чтобы быть DI?
Пропускает ли $this
параметр на вновь созданных классах плохую практику? Если да, то почему?
Ps. Я знаю, что задавать 3 вопроса в теме – это не то, что нравится stackoverflow, но я не хочу копировать весь текст, чтобы спросить их.
Вы почти там.
Нет, я не вижу его в качестве допустимого примера инъекции зависимостей. Он немного напоминает локатор сервисов (потому что вы вводите весь контейнер в свои службы и используете его для «поиска» зависимых сервисов).
Вы делаете небольшую путаницу между инъекцией зависимостей и контейнером для инъекций зависимости.
Во-первых, инъекция зависимостей означает нажатие зависимостей на объект во время выполнения, а не на создание / вытягивание.
Чтобы проиллюстрировать это:
//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
) в качестве зависимости, потому что тогда вы отказываетесь от более слабого обращения управления , а именно к локатору службы . Вместо этого вы должны передавать конкретные зависимости вашим сервисным конструкторам Есть некоторые случаи, когда вы обнаружите, что хотите передать весь $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()
Что можно улучшить? Ваш контейнер должен:
Все они сопровождаются четкой документацией об инъекции зависимостей
FrameWork_Engine_Model
– это реестр ( шаблон реестра ). Инъекция реестра как зависимость во всех объектах – это некорректная инъекция зависимостей. Технически это DI, но вы создаете зависимость от всего ко всему, а также отбираете гибкость, которую должен предложить DI. FrameWork_Engine_Model
предназначался для создания служб и управления их зависимостями, вы можете изменить его на Inversion of Control Container (типичный шаблон, связанный с DI) Я не буду спорить о вашем выборе имен классов и обязанностей ваших служб и контроллеров, так как я не думаю, что это в рамках этого вопроса. Просто замечание: похоже, что ваши контроллеры слишком много делают. Если вы заинтересованы в чистом коде, вы можете взглянуть на принцип единой ответственности и держать свои контроллеры «тонкими», перемещая бизнес-логику и запросы к базе данных на уровень обслуживания и выводить механизмы, такие как 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.