標籤:blog http 使用 os io 資料 art ar
什麼是依賴注入?
從PHP實現角度來分析依賴注入,因為PHP主要用於web開發,所以我們就看Web應用例子。
為了克服HTTP協議的無狀態性,web應用程式需要有一個途徑來在web請求之間儲存使用者資訊。最簡單的方式是使用cookie或者採用更好一點的PHP內建的Session機制。
$_SESSION[‘language‘]=‘en‘;
上面這句代碼就實現了把語言儲存到Session變數language裡。從此之後,同一使用者一些後續的請求都可以使用這個值了。因為$_SESSION 數組是個全域的變數。調用方式如下:
$user_language = $_SESSION[‘language‘];
因為依賴注入是物件導向世界裡的概念,現在我們需要把PHP Session機制封裝到一個類裡面,然後應用到web應用程式中。
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 = new User();$user->setLanguage(‘en‘);$user_language = $user->getLanguage();
如果做得更加靈活一點呢?你想改變會話cookie的名字怎麼辦?有幾個方式:可以在User類中在SessionStorage的建構函式中指定,即一種寫入程式碼方式。
class User{ function __construct() { $this->storage = new SessionStorage(‘SESSION_ID‘); } //....}
或者在User類外面定義一個常量:STORAGE_SESSION_NAME 這種全域式的常量定義,不推薦。
class User{ function __construct() { $this->storage = new SessionStorage(STORAGE_SESSION_NAME); } // ...}define(‘STORAGE_SESSION_NAME‘,‘SESSION_ID‘);
一種方式是把Session名作為User 類建構函式的參數:
class User{ function __construct($sessionName) { $this->storage = new SessionStorage($sessionName); } // ...} $user = new User(‘SESSION_ID‘);
還有一種方式是PHP代碼中常見的為Session儲存類添加一個可選設定項數組options
class User{ function __construct($storageOptions) { $this->storage = new SessionStorage($storageOptions[‘session_name‘]); } // ...} $user = new User(array(‘session_name‘ => ‘SESSION_ID‘));
實現的方法有很多啊,無論是寫入程式碼,設定為常量,作為建構函式的參數還是可選項數組都不是最佳選擇。雖然後兩者看起來好那麼一點點,但它讓User類的構造器堆積了一些跟自己沒關係的參數。
繼續,另外一個問題又來了,如果我想改變SessionStorage類怎麼辦?比如在測試時,製造個假資料。或者你想把Session儲存到資料庫或者記憶體中時,就需要改變SessionStorage類了。就目前情況來看,如果不修改User類是不可能實現改變SessionStorage類的。
現在我們考慮依賴注入,我們不在User類中建立SessionStorage對象,而是在外部建立後作為User類的構造方法參數傳遞給User對象。
class User{ function __construct($storage) { $this->storage = $storage; } // ...}
OK,這就是依賴注入,沒別的了!現在再使用User類時可能需要比上次麻煩一點:
$storage = new SessionStorage(‘SESSION_ID‘);$user = new User($storage);
現在在不改變User類的前提下,改變Session名字,改變SessionStorage類,你想幹啥都行了!
總結一下:依賴注入是組件們通過他們的構造器,方法,或者屬性欄位來擷取他們依賴的對象。
構造器注入:
class User{ function __construct($storage) { $this->storage = $storage; } // ...}
設定器注入(方法注入):
class User{ function setSessionStorage($storage) { $this->storage = $storage; } // ...}
屬性欄位注入:
class User{ public $sessionStorage;} $user->sessionStorage = $storage;
這些在Symfony 中你都會看到他們的身影:
構造注入:
$dispatcher = new sfEventDispatcher();$storage = new sfMySQLSessionStorage(array(‘database‘ => ‘session‘, ‘db_table‘ => ‘session‘));$user = new sfUser($dispatcher, $storage, array(‘default_culture‘ => ‘en‘));
將$dispatcher對象和$storage對象注入到$user對象中。
方法注入:
$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);
該注入俗稱為setter注入。
Ok,這就是依賴注入了,它會在Symfony2中發展到服務容器注入。 以提供更加方便靈活鬆散耦合的多級依賴管理。它就是Service Container,它為Symfony2的執行效率和可擴充性提供了最強大的支援。