此文是本人翻譯的來自國外某網(wǎng)站一篇文章 What is Dependency Injection?,第一次翻譯,各位見諒
這篇文章是一系列關(guān)于依賴注入和PHP輕量級容器實(shí)現(xiàn)文章中的一部分:
Part 1: What is Dependency Injection?
Part 2: Do you need a Dependency Injection Container?
Part 3: Introduction to the Symfony Service Container
Part 4: Symfony Service Container: Using a Builder to create Services
Part 5: Symfony Service Container: Using XML or YAML to describe Services
Part 6: The Need for Speed
今天,我一開始不會講容器,我希望先通過一些具體的實(shí)例來介紹一下依賴注入的理念以及其所嘗試解決的問題和它能給開發(fā)者帶來的好處。如果你已經(jīng)了解依賴注入,你可以跳過這篇文章去看下一篇。
依賴注入可能是我知道的最簡單的設(shè)計模式之一,很可能你已經(jīng)使用過,但是同時也是最難解釋的,原因可能是大多數(shù)介紹依賴注入的文章用的例子都比較無聊。我想了一個比較適合PHP領(lǐng)域的例子,因?yàn)镻HP主要用在web開發(fā),所以讓我們來看一個簡單的web實(shí)例。
為了解決http協(xié)議無狀態(tài)的問題,web應(yīng)用需要一種在web請求之間記錄用戶信息的方法,簡單的通過cookie或者session都能解決:
$_SESSION['language'] = 'fr';
上面的代碼把用戶的語言存在了session變量里面。這樣,對于同一個用戶的請求,其所使用的語言就會被存儲在$_SESSION數(shù)組里面,我們可以這樣獲取:
$user_language = $_SESSION['language'];
由于依賴注入只在面向?qū)ο蟮氖澜缋镉幸饬x,我們假裝我們有一個叫SessionStorage的類封裝了處理session的方法:
class SessionStorage
{
function __construct($cookieName = 'PHP_SESS_ID')
{
session_name($cookieName);
session_start();
}
function set($key, $value)
{
$_SESSION[$key] = $value;
}
function get($key)
{
return $_SESSION[$key];
}
// ...
}
...和一個提供高級接口的易用的User類
class User
{
protected $storage;
function __construct()
{
$this->storage = new SessionStorage();
}
function setLanguage($language)
{
$this->storage->set('language', $language);
}
function getLanguage()
{
return $this->storage->get('language');
}
// ...
}
這些類足夠簡單,使用User類也非常容易:
$user = new User();
$user->setLanguage('fr');
$user_language = $user->getLanguage();
到目前為止,一切都很好...除非你想要更多的靈活性。萬一你想要改變session里面的cookie名字呢?你可能會使用下面這些方法:
- 在SessionStorage構(gòu)造器里面硬編碼名字
class User
{
function __construct()
{
$this->storage = new SessionStorage('SESSION_ID');
}
// ...
}
- 在User類外面定義一個常量
define('STORAGE_SESSION_NAME', 'SESSION_ID');
class User
{
function __construct()
{
$this->storage = new SessionStorage(STORAGE_SESSION_NAME);
}
// ...
}
- 在User類構(gòu)造器里面?zhèn)鬟f一個名字作為參數(shù)
class User
{
function __construct($sessionName)
{
$this->storage = new SessionStorage($sessionName);
}
// ...
}
$user = new User('SESSION_ID');
- 在User類構(gòu)造器里面?zhèn)鬟f一個數(shù)組選項(xiàng)
class User
{
function __construct($storageOptions)
{
$this->storage = new SessionStorage($storageOptions['session_name']);
}
// ...
}
$user = new User(array('session_name' => 'SESSION_ID'));
以上的所有選擇都很爛,硬編碼名字沒有真正解決問題因?yàn)槟阋院罂赡茈S時會改變注意,你還得更改User類。使用常量也是一個壞注意,因?yàn)槟阌忠蕾嚵艘粋€常量。通過傳遞一個數(shù)組參數(shù)可能是一個好的解決方案,但是依然不太好,它把User構(gòu)造器和一個和它本身不相關(guān)的東西耦合了。
而且還有一個問題沒法容易搞定:我怎么換掉SessionStorage類?比方說,用一個mock對象去測試,或者你想把session保存在數(shù)據(jù)庫或內(nèi)存里面。在目前的代碼里面除非你更改User類,否則無法實(shí)現(xiàn)。
依賴注入
不要在User類里面創(chuàng)建SessionStorage對象,我們在類外面創(chuàng)建SessionStorage對象,然后通過構(gòu)造函數(shù)把其作為參數(shù)傳進(jìn)來:
class User
{
function __construct($storage)
{
$this->storage = $storage;
}
// ...
}
這就是依賴注入,就是這些!
$storage = new SessionStorage('SESSION_ID');
$user = new User($storage);
現(xiàn)在,配置一個session存儲對象非常簡單了,替換它也很容易,不用改變User類也可以實(shí)現(xiàn)其他功能。
Pico Container website 這樣形容依賴注入:“依賴注入就是通過構(gòu)造器、方法、屬性獲取所需要的元素”
依賴注入不僅僅局限于此:
- 構(gòu)造器注入:
class User
{
function __construct($storage)
{
$this->storage = $storage;
}
// ...
}
- Setter注入:
class User
{
function setSessionStorage($storage)
{
$this->storage = $storage;
}
// ...
}
- 屬性注入:
class User
{
public $sessionStorage;
}
$user->sessionStorage = $storage;
一般來說,構(gòu)造器注入最適合必要依賴,就像例子里面那樣,Setter注入比較適合可選依賴,比如說緩存對象。當(dāng)今,很多現(xiàn)代PHP框架大量使用依賴注入提供一系列既解耦又有凝聚力的組件:
// symfony: A constructor injection example
$dispatcher = new sfEventDispatcher();
$storage = new sfMySQLSessionStorage(array('database' => 'session', 'db_table' => 'session'));
$user = new sfUser($dispatcher, $storage, array('default_culture' => 'en'));
// Zend Framework: A setter injection example
$transport = new Zend_Mail_Transport_Smtp('smtp.gmail.com', array(
'auth' => 'login',
'username' => 'foo',
'password' => 'bar',
'ssl' => 'ssl',
'port' => 465,
));
$mailer = new Zend_Mail();
$mailer->setDefaultTransport($transport);
如果你想了解更多關(guān)于依賴注入的東西,我強(qiáng)烈建議你讀一讀Martin Fowler introduction 或者 Jeff More presentation。你也可以看看我去年關(guān)于依賴注入的演講,這里講了更多細(xì)節(jié)
好了,就說這么多了,我希望你現(xiàn)在對依賴注入有更好的理解,本系列的下一章我會講關(guān)于依賴注入容器