摘要:依賴注入容器管理應用程序中的全局對象包括實例化處理依賴關系。為了解決這樣的問題,我們再次回到全局注冊表創建組件。參考文章程序員如何理解依賴注入容器補充很多代碼背后,都是某種哲學思想的體現。
思想
思想是解決問題的根本
思想必須轉換成習慣
構建一套完整的思想體系是開發能力成熟的標志
——《簡單之美》(前言)
.
術語介紹“成功的軟件項目就是那些提交產物達到或超出客戶的預期的項目,而且開發過程符合時間和費用上的要求,結果在面對變化和調整時有彈性。”
——《面向對象分析與設計》(第3版)P.236
——引用《Spring 2.0 技術手冊》林信良
非侵入性 No intrusive框架的目標之一是非侵入性(No intrusive)
組件可以直接拿到另一個應用或框架之中使用
增加組件的可重用性(Reusability)
容器(Container)管理對象的生成、資源取得、銷毀等生命周期
建立對象與對象之間的依賴關系
啟動容器后,所有對象直接取用,不用編寫任何一行代碼來產生對象,或是建立對象之間的依賴關系。
IoC控制反轉 Inversion of Control
依賴關系的轉移
依賴抽象而非實踐
DI依賴注入 Dependency Injection
不必自己在代碼中維護對象的依賴
容器自動根據配置,將依賴注入指定對象
AOPAspect-oriented programming
面向方面編程
無需修改任何一行程序代碼,將功能加入至原先的應用程序中,也可以在不修改任何程序的情況下移除。
分層代碼演示IoC表現層:提供服務,顯示信息。
領域層:邏輯,系統中真正的核心。
數據源層:與數據庫、消息系統、事務管理器及其它軟件包通信。
——《企業應用架構模式》P.14
假設應用程序有儲存需求,若直接在高層的應用程序中調用低層模塊API,導致應用程序對低層模塊產生依賴。
/** * 高層 */ class Business { private $writer; public function __construct() { $this->writer = new FloppyWriter(); } public function save() { $this->writer->saveToFloppy(); } } /** * 低層,軟盤存儲 */ class FloppyWriter { public function saveToFloppy() { echo __METHOD__; } } $biz = new Business(); $biz->save(); // FloppyWriter::saveToFloppy
假設程序要移植到另一個平臺,而該平臺使用USB磁盤作為存儲介質,則這個程序無法直接重用,必須加以修改才行。本例由于低層變化導致高層也跟著變化,不好的設計。
正如前方提到的
控制反轉 Inversion of Control
依賴關系的轉移
依賴抽象而非實踐
程序不應該依賴于具體的實現,而是要依賴抽像的接口。請看代碼演示
/** * 接口 */ interface IDeviceWriter { public function saveToDevice(); } /** * 高層 */ class Business { /** * @var IDeviceWriter */ private $writer; /** * @param IDeviceWriter $writer */ public function setWriter($writer) { $this->writer = $writer; } public function save() { $this->writer->saveToDevice(); } } /** * 低層,軟盤存儲 */ class FloppyWriter implements IDeviceWriter { public function saveToDevice() { echo __METHOD__; } } /** * 低層,USB盤存儲 */ class UsbDiskWriter implements IDeviceWriter { public function saveToDevice() { echo __METHOD__; } } $biz = new Business(); $biz->setWriter(new UsbDiskWriter()); $biz->save(); // UsbDiskWriter::saveToDevice $biz->setWriter(new FloppyWriter()); $biz->save(); // FloppyWriter::saveToDevice
控制權從實際的FloppyWriter轉移到了抽象的IDeviceWriter接口上,讓Business依賴于IDeviceWriter接口,且FloppyWriter、UsbDiskWriter也依賴于IDeviceWriter接口。
這就是IoC,面對變化,高層不用修改一行代碼,不再依賴低層,而是依賴注入,這就引出了DI。
比較實用的注入方式有三種:
Setter injection 使用setter方法
Constructor injection 使用構造函數
Property Injection 直接設置屬性
事實上不管有多少種方法,都是IoC思想的實現而已,上面的代碼演示的是Setter方式的注入。
依賴注入容器 Dependency Injection Container管理應用程序中的『全局』對象(包括實例化、處理依賴關系)。
可以延時加載對象(僅用到時才創建對象)。
促進編寫可重用、可測試和松耦合的代碼。
理解了IoC和DI之后,就引發了另一個問題,引用Phalcon文檔描述如下:
如果這個組件有很多依賴, 我們需要創建多個參數的setter方法??來傳遞依賴關系,或者建立一個多個參數的構造函數來傳遞它們,另外在使用組件前還要每次都創建依賴,這讓我們的代碼像這樣不易維護
//創建依賴實例或從注冊表中查找 $connection = new Connection(); $session = new Session(); $fileSystem = new FileSystem(); $filter = new Filter(); $selector = new Selector(); //把實例作為參數傳遞給構造函數 $some = new SomeComponent($connection, $session, $fileSystem, $filter, $selector); // ... 或者使用setter $some->setConnection($connection); $some->setSession($session); $some->setFileSystem($fileSystem); $some->setFilter($filter); $some->setSelector($selector);
假設我們必須在應用的不同地方使用和創建這些對象。如果當你永遠不需要任何依賴實例時,你需要去刪掉構造函數的參數,或者去刪掉注入的setter。為了解決這樣的問題,我們再次回到全局注冊表創建組件。不管怎么樣,在創建對象之前,它增加了一個新的抽象層:
class SomeComponent { // ... /** * Define a factory method to create SomeComponent instances injecting its dependencies */ public static function factory() { $connection = new Connection(); $session = new Session(); $fileSystem = new FileSystem(); $filter = new Filter(); $selector = new Selector(); return new self($connection, $session, $fileSystem, $filter, $selector); } }
瞬間,我們又回到剛剛開始的問題了,我們再次創建依賴實例在組件內部!我們可以繼續前進,找出一個每次能奏效的方法去解決這個問題。但似乎一次又一次,我們又回到了不實用的例子中。
一個實用和優雅的解決方法,是為依賴實例提供一個容器。這個容器擔任全局的注冊表,就像我們剛才看到的那樣。使用依賴實例的容器作為一個橋梁來獲取依賴實例,使我們能夠降低我們的組件的復雜性:
class SomeComponent { protected $_di; public function __construct($di) { $this->_di = $di; } public function someDbTask() { // 獲得數據庫連接實例 // 總是返回一個新的連接 $connection = $this->_di->get("db"); } public function someOtherDbTask() { // 獲得共享連接實例 // 每次請求都返回相同的連接實例 $connection = $this->_di->getShared("db"); // 這個方法也需要一個輸入過濾的依賴服務 $filter = $this->_di->get("filter"); } } $di = new PhalconDI(); //在容器中注冊一個db服務 $di->set("db", function() { return new Connection(array( "host" => "localhost", "username" => "root", "password" => "secret", "dbname" => "invo" )); }); //在容器中注冊一個filter服務 $di->set("filter", function() { return new Filter(); }); //在容器中注冊一個session服務 $di->set("session", function() { return new Session(); }); //把傳遞服務的容器作為唯一參數傳遞給組件 $some = new SomeComponent($di); $some->someTask();
這個組件現在可以很簡單的獲取到它所需要的服務,服務采用延遲加載的方式,只有在需要使用的時候才初始化,這也節省了服務器資源。這個組件現在是高度解耦。例如,我們可以替換掉創建連接的方式,它們的行為或它們的任何其他方面,也不會影響該組件。
參考文章PHP程序員如何理解依賴注入容器(dependency injection container)
http://docs.phalconphp.com/zh/latest/reference/di.html
What is Dependency Injection? Fabien Potencier
Inversion of Control Containers and the Dependency Injection pattern by Martin Fowler
補充很多代碼背后,都是某種哲學思想的體現。
以下引用《面向模式的軟件架構》卷1模式系統第六章模式與軟件架構
軟件架構支持技術(開發軟件時要遵循的基本原則)抽象
封裝
信息隱藏
分離關注點
耦合與內聚
充分、完整、簡單
策略與實現分離
策略組件負責上下文相關決策,解讀信息的語義和含義,將眾多不同結果合并或選擇參數值
實現組件負責執行定義完整的算法,不需要作出與上下文相關的決策。上下文和解釋是外部的,通常由傳遞給組件的參數提供。
接口與實現分離
接口部分定義了組件提供的功能以及如何使用該組件。組件的客戶端可以訪問該接口。
實現部分包含實現組件提供的功能的實際代碼,還可能包含僅供組件內部使用的函數和數據結構。組件的客戶端不能訪問其實現部分。
單個引用點
軟件系統中的任何元素都應只聲明和定義一次,避免不一致性問題。
10. 分而治之
可修改性
可維護性
可擴展性
重組
可移植性
互操作性
與其它系統或環境交互
效率
可靠性
容錯:發生錯誤時確保行為正確并自行修復
健壯性:對應用程序進行保護,抵御錯誤的使用方式和無效輸入,確保發生意外錯誤時處于指定狀態。
可測試性
可重用性
通過重用開發軟件
開發軟件時考慮重用
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/20888.html
摘要:代碼這就是控制反轉模式。是變量有默認值則設置默認值是一個類,遞歸解析有默認值則返回默認值從容器中取得以上代碼的原理參考官方文檔反射,具有完整的反射,添加了對類接口函數方法和擴展進行反向工程的能力。 PHP程序員如何理解依賴注入容器(dependency injection container) 背景知識 傳統的思路是應用程序用到一個Foo類,就會創建Foo類并調用Foo類的方法,假如這...
摘要:前言最近在使用框架,看了下他的源碼,發現有很多地方也用到了依賴注入控制反轉,覺得有必要和大家簡單聊一聊什么是依賴注入以及怎么使用它。概念依賴注入和控制反轉是對同一件事情的不同描述,從某個方面講,就是它們描述的角度不同。 前言 最近在使用ThinkPHP5框架,看了下他的源碼,發現有很多地方也用到了依賴注入(控制反轉),覺得有必要和大家簡單聊一聊什么是依賴注入以及怎么使用它。 簡介 I...
摘要:標量參數關聯傳值依賴是自動解析注入的,剩余的標量參數則可以通過關聯傳值,這樣比較靈活,沒必要把默認值的參數放在函數參數最尾部。 更新:github(給個小星星呀) -- 2018-4-11:優化服務綁定方法 ::bind 的類型檢查模式 借助 PHP 反射機制實現的一套 依賴自動解析注入 的 IOC/DI 容器,可以作為 Web MVC 框架 的應用容器 1、依賴的自動注入:你只需要...
摘要:當你需要用到某個對象時,就可以使用如下代碼從容器中獲取獲取容器獲取容器中的對象管理方式的所創建與存儲的對象,我們稱之為屬性注入方式管理注解方式屬性注入注解方式 好處 IoC / DI,方便解耦 AOP 面向切面編程 聲明式事務 方便程序測試 方便集成其他優秀框架 IoC 與 DI IoC 與 DI 的關系 IoC控制反轉:主要是指【創建對象】這件事交給Spring進行處理,無需開發...
摘要:服務本省作為一個高層類,對外提供訪問,卻受制于提供具體服務的服務提供者定義的實現,高層模塊依賴底層模塊實現,違背了依賴倒置原則。遵循依賴倒置原則的例子場景同介紹中場景。 1. 名詞介紹 OOD,面向對象設計 DIP,依賴倒置(軟件設計原則) IOC,控制反轉(軟件設計模式) DI,依賴注入 IOC Container,控制反轉容器,也是依賴注入容器 2. 組成部分 服務清單(功能...
閱讀 1185·2023-04-25 17:05
閱讀 3011·2021-11-19 09:40
閱讀 3544·2021-11-18 10:02
閱讀 1740·2021-09-23 11:45
閱讀 3022·2021-08-20 09:36
閱讀 2783·2021-08-13 15:07
閱讀 1133·2019-08-30 15:55
閱讀 2459·2019-08-30 14:11