摘要:依賴注入并不限于構造函數作為經驗,注入最適合必須的依賴關系,比如示例中的情況注入最適合可選依賴關系,比如緩存一個對象實例。
本文翻譯自 Symfony 作者 Fabien Potencier 的 《Dependency Injection in general and the implementation of a Dependency Injection Container in PHP》 系列文章。
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
依賴注入 設計模式非常簡單,但又很難解釋清楚。造成這個現象的主要原因是,別的介紹 依賴注入 的文章里太多廢話,讓人混淆。下面我將通過一些更適合 PHP 的例子來講解它。
HTTP 協議是無狀態的,我們的 Web 應用程序如果需要在請求之間存儲用戶信息,可以通過 COOKIE 或 SESSION :
$_SESSION["language"] = "fr";
上述代碼中,我們將 language 存儲在全局變量 $_SESSION 中,因此可以這樣獲取它:
$user_language = $_SESSION["language"];
只有在 OOP 開發時中才會遇到 依賴注入 ,因此假設我們有一個封裝 SESSION 的 SessionStorage 類:
class SessionStorage { function __construct($cookieName="PHPSESSID") { 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("fr"); $user_language = $user->getLanguage();
這種方式看起來很完美,但是并不夠靈活。比如:現在想修改會話的 COOKIE 名稱(默認為 PHPSESSID) ,怎么辦?這時有一大堆辦法:
把會話的 COOKIE 名稱寫死在 User 類中 SessionStorage 的構造函數中 (Hardcode):
class User { function __construct() { $this->storage = new SessionStorage("SESSION_ID"); } // ... }
或者在 User 類外面定義一個常量:
class User { function __construct() { $this->storage = new SessionStorage(SESSION_COOKIE_NAME); } // ... } define("SESSION_COOKIE_NAME", "SESSION_ID");
或者把會話的 COOKIE 名稱作為 User 類構造函數的一個參數傳進去:
class User { function __construct($cookieName) { $this->storage = new SessionStorage($cookieName); } // ... } $user = new User("SESSION_ID");
或者給 SessionStorage 類加個選項數組:
class User { function __construct($storageOptions) { $this->storage = new SessionStorage($storageOptions["cookie_name"]); } // ... } $user = new User(["cookie_name" => "SESSION_ID"]);
上述方法都很糟糕:
把會話的 COOKIE 名稱寫死的話,每次想再改名,都要修改 User 類
使用常量的話,User 類的變化將取決于設置的常量
使用參數或者選項數組看起來很靈活,但它把與 User 本身無關的東西摻雜在了構造函數中
通過構造函數,把一個外部的 SessionStorage 實例"注入"進 User 實例內部,而不是在 User 實例內部創建 SessionStorage 實例,就是 依賴注入。class User { function __construct($storage) { $this->storage = $storage; } // ... }
很清爽吧!只需先創建 SessionStorage 實例,再創建 User 實例:
$storage = new SessionStorage("SESSION_ID"); $user = new User($storage);
用這個方法,配置 SessionStorage 很簡單,給 User 替換 $storage 類型也很簡單,都不需要去修改 User 類。這就實現了解耦。
依賴注入 并不限于構造函數:
Constructor Injection:
class User { function __construct($storage) { $this->storage = $storage; } // ... }
Setter Injection:
class User { function setSessionStorage($storage) { $this->storage = $storage; } // ... }
Property Injection:
class User { public $sessionStorage; } $user->sessionStorage = $storage;
作為經驗, Constructor 注入 最適合必須的依賴關系,比如示例中的情況; Setter 注入 最適合可選依賴關系,比如緩存一個對象實例。
現在,大多數現代 PHP 框架都大量使用依賴注入來提供一組 去耦 但 粘合 的組件:
// symfony: A constructor injection example $dispatcher = new sfEventDispatcher(); $storage = new sfMySQLSessionStorage([ "database" => "session", "db_table" => "session", ]); $user = new sfUser($dispatcher, $storage, ["default_culture" => "en"]); // Zend Framework: A setter injection example $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);
如果你有興趣了解更多有關 依賴注入 的信息,我強烈建議你閱讀 Martin Fowler 的介紹 或 Jeff Moore 的 PPT。你還可以看看我去年關于 依賴注入 的 PPT,其中我詳細了介紹本文中所討論的示例。
希望本文能讓你更好的理解 依賴注入,在本系列的下一部分中,我將討論 依賴注入容器 (Dependency Injection Containers)。
原創。 所有 Laravel 文章均已收錄至 laravel-tips 項目。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/25954.html
摘要:上文書,創建對象需要先創建對象。創建對象的雜活是嵌入在中的。對象使用來管理依賴關系非常好,但不是必須的。很容易實現,但手工維護各種亂七八糟的對象還是很麻煩。所有文章均已收錄至項目。 本文翻譯自 Symfony 作者 Fabien Potencier 的 《Dependency Injection in general and the implementation of a Depend...
摘要:意味著依賴被注入進構造函數或者方法如果需要復用實例,可以定義為單例可以用接口或任何名稱來代替具體類。技能重寫構造函數參數方法允許將附加參數傳遞給構造函數。 本文大部分翻譯自 DAVE JAMES MILLER 的 《Laravel’s Dependency Injection Container in Depth》 。 上文介紹了 Dependency Injection Contai...
摘要:引述最近看設計模式以及代碼,對于控制反轉以及依賴注入這些概念非常困惑,于是找了一些資料,以下是對于控制反轉的一下理解。其中最常見的方式叫做依賴注入,簡稱,還有一種方式叫依賴查找。在軟件工程中,依賴注入是種實現控制反轉用于解決依賴性設計模式。 引述 最近看設計模式以及laravel代碼,對于控制反轉以及依賴注入這些概念非常困惑,于是找了一些資料,以下是對于控制反轉的一下理解。 概念 Io...
摘要:劃下重點,服務容器是用于管理類的依賴和執行依賴注入的工具。類的實例化及其依賴的注入,完全由服務容器自動的去完成。 本文首發于 深入剖析 Laravel 服務容器,轉載請注明出處。喜歡的朋友不要吝嗇你們的贊同,謝謝。 之前在 深度挖掘 Laravel 生命周期 一文中,我們有去探究 Laravel 究竟是如何接收 HTTP 請求,又是如何生成響應并最終呈現給用戶的工作原理。 本章將帶領大...
摘要:依賴注入和控制反轉,這兩個詞經常一起出現。一句話表述他們之間的關系依賴注入是控制反轉的一種實現方式。而兩者有大量的代碼都是可以共享的,這就是依賴注入的使用場景了。下一步就是創建具體的依賴內容,然后注入到需要的地方這里的等于這個對象。 前言 React 是一個十分龐大的庫,由于要同時考慮 ReactDom 和 ReactNative ,還有服務器渲染等,導致其代碼抽象化程度很高,嵌套層級...
閱讀 3477·2021-11-08 13:30
閱讀 3584·2019-08-30 15:55
閱讀 688·2019-08-29 15:16
閱讀 1750·2019-08-26 13:57
閱讀 2090·2019-08-26 12:18
閱讀 789·2019-08-26 11:36
閱讀 1733·2019-08-26 11:30
閱讀 3017·2019-08-23 16:46