【发布时间】:2012-05-14 18:03:55
【问题描述】:
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);
【问题讨论】:
-
这是一个非常好的问题,但与 Stack Overflow 相比,它似乎更适合程序员。
-
任何提示我如何将它转移到那里? O-)
-
您是否在寻找延迟加载/初始化? Advanced OO Patterns (video)
标签: php design-patterns dependency-injection