DI 背后的要点是让一个类从创建和准备它所依赖的对象中解脱出来,然后将它们推入。这听起来很合理,但有时一个类不需要所有被推入其中的对象来执行其功能。这背后的原因是“提前返回”发生在无效的用户输入或所需对象之一较早抛出的异常或在代码块运行之前实例化对象所需的某个值不可用时。
更实际的例子:
- 注入一个永远不会使用的数据库连接对象,因为用户数据没有通过验证(前提是没有使用触发器来验证这个数据)
- 注入收集输入的类似excel的对象(例如PHPExcel)(加载和实例化很重,因为整个库被拉入并且从未使用过,因为验证在写入发生之前抛出异常)
- 在类中确定的变量值,但不是在运行时的注入器;例如,一个路由组件,它确定应该根据用户输入调用的控制器(或命令)类和方法
- 虽然这可能是一个设计问题,但它是一个实质性的服务类,它依赖于很多组件,但每个请求只使用其中的 1/3(原因,为什么我倾向于使用命令类而不是控制器)
因此,在某种程度上,推入所有必要的组件与创建某些组件但从未使用过的“延迟加载”相矛盾,这有点不切实际并且会影响性能。就 PHP 而言 - 加载、解析和编译更多文件。如果被推入的对象有自己的依赖关系,这尤其痛苦。
我看到了 3 种方法,其中 2 种听起来不太好:
- 注入工厂
- 注入注射器(一种反模式)
- 注入一些外部函数,一旦达到相关点就会从类内部调用(smtg,如“数据验证完成后检索 PHPExcel 实例”);由于它的灵活性,这是我倾向于使用的
问题是处理这种情况的最佳方法是什么/你们使用什么?
更新:@GordonM 以下是 3 种方法的示例:
//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);