本文翻译自
Symfony
做者 Fabien Potencier 的 《Dependency Injection in general and the implementation of a Dependency Injection Container in PHP》 系列文章。php
专有名词翻译成中文后会变得不利于理解,后续文章中将改用括号+中文备注的形式。html
上文我经过一些示例讲解了 Dependency Injection
,本文将接着介绍 Dependency Injection Containers (容器)
的概念。laravel
首先记住这句话:git
大多数时候,
Dependency Injection
并不须要Container
。github
只有当你须要管理一大堆具备不少依赖关系的不一样对象时,Container
才会很是有用(例如框架中)。 框架
上文书,建立 User
对象须要先建立 SessionStorate
对象。这里的有个瑕疵,建立对象时须要提早知道它全部的依赖项:函数
$storage = new SessionStorage('SESSION_ID'); $user = new User($storage);
以 Zend Framework
中 Zend_Mail
库发送邮件过程为例:ui
$transport = new Zend_Mail_Transport_Smtp('smtp.gmail.com', [ 'auth' => 'login', 'username' => 'foo', 'password' => 'bar', 'ssl' => 'ssl', 'port' => 465, ]); $mailer = new Zend_Mail(); $mailer->setDefaultTransport($transport);
请把这个例子看作一个大系统中的一小部分,由于这种简单的例子固然不必用
Container
。this
Dependency Injection Container
是一个“知道如何实例化和配置对象”的对象(工厂模式的升华)。为了作到这点,它须要知道构造函数的参数、以及对象之间的关系。翻译
下面是一个写死 Zend_Mail
的 Container
:
class Container { public function getMailTransport() { return new Zend_Mail_Transport_Smtp('smtp.gmail.com', [ 'auth' => 'login', 'username' => 'foo', 'password' => 'bar', 'ssl' => 'ssl', 'port' => 465, ]); } public function getMailer() { $mailer = new Zend_Mail(); $mailer->setDefaultTransport($this->getMailTransport()); return $mailer; } }
这个 Container
用起来就至关简单了:
$container = new Container(); $mailer = $container->getMailer();
咱们只管向 Container
要 mailer
对象就行,彻底不用管 mailer
怎么建立。建立 mailer
对象的“杂活”是嵌入在 Container
中的。Container
经过 getMailTransport()
方法,把 Zend_Mail_Transport_Smtp
这个依赖自动注入到了 Zend_Mail
中。
细心的网友可能已经发现,这里的 Container
把什么都写死了。咱们能够完善一下:
class Container { protected $parameters = array(); public function __construct(array $parameters = []) { $this->parameters = $parameters; } public function getMailTransport() { return new Zend_Mail_Transport_Smtp('smtp.gmail.com', [ 'auth' => 'login', 'username' => $this->parameters['mailer.username'], 'password' => $this->parameters['mailer.password'], 'ssl' => 'ssl', 'port' => 465, ]); } public function getMailer() { $mailer = new Zend_Mail(); $mailer->setDefaultTransport($this->getMailTransport()); return $mailer; } }
如今就能够随时更改 username
和 password
了:
$container = new Container([ 'mailer.username' => 'foo', 'mailer.password' => 'bar', ]); $mailer = $container->getMailer();
若是须要更改 mailer
类,把类名也当参数传入就行:
class Container { // ... public function getMailer() { $class = $this->parameters['mailer.class']; $mailer = new $class(); $mailer->setDefaultTransport($this->getMailTransport()); return $mailer; } } $container = new Container([ 'mailer.username' => 'foo', 'mailer.password' => 'bar', 'mailer.class' => 'Zend_Mail', ]); $mailer = $container->getMailer();
若是想每次获取同一个 mailer
实例,能够用 单例模式
:
class Container { static protected $shared = []; // ... public function getMailer() { if (isset(self::$shared['mailer'])) { return self::$shared['mailer']; } $class = $this->parameters['mailer.class']; $mailer = new $class(); $mailer->setDefaultTransport($this->getMailTransport()); return self::$shared['mailer'] = $mailer; } }
这就包含了 Dependency Injection Containers
的基本功能:
Container
管理对象实例化到配置的过程Container
管理的,对 Container
一无所知。这就是为何 Container
可以管理任何 PHP 对象。 对象使用 DI
来管理依赖关系很是好,但不是必须的。
Container
很容易实现,但手工维护各类乱七八糟的对象仍是很麻烦。下一章我将介绍 Laravel
中 Container
的实现方式。
做者下一章原文中讲的是
Container
在Symfony 2
中的实现,我会把它换成Laravel
。
原创。 全部 Laravel 文章均已收录至 laravel-tips 项目。