Суть за DI заключается в том, чтобы освободить класс от создания и подготовки объектов, от которых он зависит, и подталкивать их. Это звучит очень разумно, но иногда классу не нужны все объекты, которые вставляются в него для выполнения его функции. Причиной этого является "раннее возвращение", которое происходит при некорректном вводе пользователя или исключении, вызванном одним из требуемых объектов раньше, или недоступности определенного значения, необходимого для создания экземпляра объекта до тех пор, пока не будет выполняться блок кода.
Более практические примеры:
- вводить объект подключения к базе данных, который никогда не будет использоваться, поскольку пользовательские данные не проходят проверку (при условии, что для проверки этих данных не используются триггеры).
- вводящие excel-подобные объекты (PHPExcel, например), которые собирают входные данные (тяжелые для загрузки и создания экземпляра, потому что вся библиотека втягивается и никогда не используется, поскольку проверка делает исключение раньше, чем происходит запись)
- значение переменной, определенное в классе, но не инжектор во время выполнения; например, компонент маршрутизации, который определяет класс и метод контроллера (или команды), который должен вызываться на основе пользовательского ввода
- хотя это может быть проблема проектирования, но существенный класс обслуживания, который зависит от множества компонентов, но использует только 1/3 из них для каждого запроса (причина, почему я склонен использовать командные классы вместо контроллеры)
Таким образом, толкание всех необходимых компонентов противоречит "ленивой загрузке" таким образом, что некоторые компоненты создаются и никогда не используются, что немного непрактично и влияет на производительность. Что касается PHP, то больше загружаются файлы, анализируются и компилируются. Это особенно болезненно, если объекты, в которые вставляются, имеют свои собственные зависимости.
я вижу 3 пути вокруг него, 2 из которых звучат не очень хорошо:
- вводя factory
- инъекция инжектора (анти-шаблон)
- впрыскивание некоторой внешней функции, вызываемой изнутри класса, как только будет достигнута соответствующая точка (smtg like "retrieve a Экземпляр PHPExcel после завершения проверки данных "), это то, что я склонны использовать из-за своей гибкости.
Вопрос в том, что лучший способ справиться с такими ситуациями/что вы, ребята, используете?
UPDATE: @GordonM вот примеры из трех подходов:
//inject factory example
interface IFactory{
function factory();
}
class Bartender{
protected $_factory;
public function __construct(IFactory $f){
$this->_factory = $f;
}
public function order($data){
//validating $data
//... return or throw exception
//validation passed, order must be saved
$db = $this->_factory->factory(); //! factory instance * num necessary components
$db->insert('orders', $data);
//...
}
}
/*
inject provider example
assuming that the provider prepares necessary objects
(i.e. injects their dependencies as well)
*/
interface IProvider{
function get($uid);
}
class Router{
protected $_provider;
public function __construct(IProvider $p){
$this->_provider = $p;
}
public function route($str){
//... match $str against routes to resolve class and method
$inst = $this->_provider->get($class);
//...
}
}
//inject callback (old fashion way)
class MyProvider{
protected $_db;
public function getDb(){
$this->_db = $this->_db ? $this->_db : new mysqli();
return $this->_db;
}
}
class Bartender{
protected $_db;
public function __construct(array $callback){
$this->_db = $callback;
}
public function order($data){
//validating $data
//... return or throw exception
//validation passed, order must be saved
$db = call_user_func_array($this->_db, array());
$db->insert('orders', $data);
//...
}
}
//the way it works under the hood:
$provider = new MyProvider();
$db = array($provider, 'getDb');
new Bartender($db);
//inject callback (the PHP 5.3 way)
class Bartender{
protected $_db;
public function __construct(Closure $callback){
$this->_db = $callback;
}
public function order($data){
//validating $data
//... return or throw exception
//validation passed, order must be saved
$db = call_user_func_array($this->_db, array());
$db->insert('orders', $data);
//...
}
}
//the way it works under the hood:
static $conn = null;
$db = function() use ($conn){
$conn = $conn ? $conn : new mysqli();
return $conn;
};
new Bartender($db);