您好,登錄后才能下訂單哦!
這篇文章是關于一般依賴關系注入和在PHP中實現依賴注入容器系列的第一部分。
今天我不會談論容器然而我想以一些具體的示例介紹依賴注入的概念希望說明嘗試去解決問題和它給開發者帶來的好處。如果你已經知道依賴注入的概念你可以略過本文等待下一篇。
依賴注入或許是我所知道的最簡單的設計模式之一。或許你已經使用過依賴注入。但它是解釋最困難的一個。我認為部分原因在于大多數介紹依賴注入的時候使用了沒有意義的示例。我試圖想出更符合PHP世界的例子由于PHP這種語言主要是用來做WEB開發的讓我們舉一個簡單的WEB例子。
為了克服HTTP協議的無狀態性web應用程序需要一種方法來存儲web請求之間的用戶信息。當然通過使用一個cookie或者甚至更好的方式通過使用PHP內置的會話機制(session)實現是非常簡單的。
$_SESSION['language'] = 'fr';
上面的代碼中用語言會話變量存儲用戶的語言。因此針對同一個用戶的后續請求在超全局變量$_SESSION數組中的語言是有效的。
$user_language = $_SESSION['language'];
在面向對象的世界中依賴關系注入才有意義讓我們假設我們有一個SessionStorage 類封裝了PHP的會話機制
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]; } // ... }
... 和一個提供了更高級別接口的用戶類
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();
全部都很好...直到你想要更多的靈活性。如果你想要修改實例會話cookie的名稱下面是一些隨機的可能性
·在User類里面是硬編碼的名稱在他的SessionStorage 構造函數中:
class User { function __construct() { $this->storage = new SessionStorage('SESSION_ID'); } // ... }
在User類的外面定義一個常量
class User { function __construct() { $this->storage = new SessionStorage(STORAGE_SESSION_NAME); } // ... } define('STORAGE_SESSION_NAME', 'SESSION_ID');
添加會話的名稱session name作為User類的構造函數的參數
class User { function __construct($sessionName) { $this->storage = new SessionStorage($sessionName); } // ... } $user = new User('SESSION_ID');
為存儲類SessionStorage添加一個選項數組
class User { function __construct($storageOptions) { $this->storage = new SessionStorage($storageOptions['session_name']); } // ... } $user = new User(array('session_name' => 'SESSION_ID'));
所有的這些選擇都是相當糟糕的。在User類中的硬編碼會話名稱session name并沒有真正解決問題你不能輕易改變想法以后而無需再次更改User類。使用一個常量之所以糟糕的因為User類現在依賴一個常量被設置。傳遞會話名稱session name作為一個參數或者作為一個選項數組是或許是最好的解決方法但它仍然很糟糕。用不相關的參數給對象本身打亂了User類構造函數的參數。
但是仍然有問題不能被解決我怎么才能更改SessionStorage 類呢例如為了方便測試將它替換為一個假對象。或者也許是因為你想要在數據庫表中或者內存中存儲會話sessions。當前實現是不可能的除非你修改User類。加入依賴注入而不是在User類里面建立SessionStorage 對象讓我們在User對象通過傳遞一個構造參數注入SessionStorage對象
class User { function __construct($storage) { $this->storage = $storage; } // ... }
這就是依賴注入。僅此而已使用User類現在多了一點包括你首先需要建立SessionStorage 對象
$storage = new SessionStorage('SESSION_ID'); $user = new User($storage);
現在配置會話存儲對象session storage object是非常簡單的和替換會話存儲類也是非常容易。并且由于關注點的分離User類沒有改變一切都是可能的。
Pico Container website介紹的依賴注入就像這樣的
"Dependency Injection is where components are given their dependencies through their constructors, methods, or directly into fields."
“依賴注入是其中的組件通過他們的構造器、方法、或者直接到字段的方式”給與他們的依賴關系。
至于其他的設計模式依賴注入也有一些反模式。Pico Container website介紹了其中的一些。
依賴注入不局限于構造函數注入:
·構造器注入:
class User { function __construct($storage) { $this->storage = $storage; } // ... }
·Setter注入:
class User { function setSessionStorage($storage) { $this->storage = $storage; } // ... }
·屬性注入:
class User { public $sessionStorage; } $user->sessionStorage = $storage;
作為經驗法則構造器所需要的依賴關系是最好的就像在我們的例子中setter注入最適合可選的依賴關系比如像一個緩存實例對象。如今最現代化的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);
如果你有興趣學習更多關于依賴注入我強烈建議你閱讀Martin Fowler introduction或者優秀的Jeff More presentation。你還可以去看看我去年給依賴注入做的介紹presentation在這篇文章中談論的在里面講了更多在例子上的細節。
今天就到這了。我希望你現在對依賴注入的概念有一個更好的理解。在本系列的下一篇文章我會談談依賴注入的容器。
原文地址http://fabien.potencier.org/article/11/what-is-dependency-injection
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。