本文翻譯自
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 項目。