你快到了。
问题1
不,我不认为它是一个有效的依赖注入示例。它有点类似于服务定位器(因为您将整个容器注入到您的服务中并使用它来“定位”依赖服务)。
问题2
您在依赖项注入和依赖项注入容器之间造成了一些混淆。
首先,依赖注入意味着在运行时将依赖项推送到对象中,而不是创建/拉取它们。
举个例子:
//hardcoded dependecies
class BadService
{
public function __construct()
{
$this->dep1 = new ConcreteObject1();
$this->dep2 = new ConcreteObject2();
}
}
所以在上面的例子中,BadService
使得无法在运行时连接其他依赖项,因为它们已经被硬拉到构造函数本身中。
//service locator pattern
class AlmostGoodService
{
public function __construct(Container $container)
{
$this->dep1 = $container->getADep1();
$this->dep2 = $container->getADep2();
}
}
In the AlmostGoodService
例如,我们已经从前面的示例中删除了硬依赖项,但我们仍然依赖于容器的特定实现(这意味着如果不提供该容器的实现,我们的服务就无法重用)。这是与您正在做的事情相匹配的示例。
//dependecy injection
class GoodService
{
public function __construct($dep1, OptionalInterface $dep2)
{
$this->dep1 = $dep1;
$this->dep2 = $dep2;
}
}
The GoodService
服务不关心其具体依赖项的创建,并且可以在运行时轻松地与实现服务“协议”的任何依赖项“连接”$dep1
或可选接口$dep2
(因此名称为控制反转 http://en.wikipedia.org/wiki/Inversion_of_control- 依赖注入背后的基本概念)。
进行这种接线的组件称为依赖注入容器 http://fabien.potencier.org/article/12/do-you-need-a-dependency-injection-container.
Now, a 依赖注入容器 http://fabien.potencier.org/article/12/do-you-need-a-dependency-injection-container最简单的形式只不过是一个能够在运行时根据某种形式的配置连接对象的对象。
我说你已经差不多了,但是你的实现存在一些问题:
- 接线应该是惰性的(您不想让所有这些在您的构造函数中工作,因为您的应用程序随着它的增长会大大减慢)
- 你不应该传递整个容器(
$this
)作为依赖项,因为这样你就会回退到较弱的控制反转 http://en.wikipedia.org/wiki/Inversion_of_control,即一个服务定位器 http://en.wikipedia.org/wiki/Service_locator_pattern。您应该将具体的依赖项传递给您的服务构造函数
问题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()
有什么可以改进的地方?您的容器应该:
- 提供一种共享服务的方式 - 即仅初始化一次
- be lockable https://github.com/catacgc/juice-di-container#locking-behaviour- 提供配置后锁定的方法
- 能够与其他容器“合并” - 这样您的应用程序将真正模块化
- allow 可选的依赖项 http://symfony.com/doc/2.1/book/service_container.html#making-references-optional
- allow scopes http://symfony.com/doc/2.0/cookbook/service_container/scopes.html
- support 标签服务 http://symfony.com/doc/2.1/book/service_container.html#tags
准备使用 DI 容器实现
所有这些都附有关于依赖注入 http://en.wikipedia.org/wiki/Dependency_injection
-
Pimple http://pimple.sensiolabs.org/- PHP 5.3 轻量级 DI 容器
-
Symfony2 DI 容器 http://symfony.com/doc/master/components/dependency_injection/introduction.html- PHP 5.3 具有完整的 DI 容器功能
-
Juice DI https://github.com/catacgc/juice-di-container- 小型 PHP 5.2 DI 容器