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