依賴注入(Dependency Injection,簡稱DI)又稱控制反轉(zhuǎn)(Inversion of Control,簡稱IOC),在目前的主流框架中,如ThinkPHP、Laravel等,都有實現(xiàn)。
什么是依賴注入呢?
舉個簡單的例子:
(1)原始社會里,幾乎沒有社會分工。需要斧子的人(調(diào)用者)只能自己去磨一把斧子(被調(diào)用者)。
(2)進入工業(yè)社會,工廠出現(xiàn)。斧子不再由普通人完成,而在工廠里被生產(chǎn)出來,此時需要斧子的人(調(diào)用者)找到工廠,購買斧子,無須關(guān)心斧子的制造過程。
(3)進入“按需分配”社會,需要斧子的人不需要找到工廠,坐在家里發(fā)出一個簡單指令:需要斧子。斧子就自然出現(xiàn)在他面前。

第一種情況下,實例的調(diào)用者創(chuàng)建被調(diào)用的實例,必然要求被調(diào)用的類出現(xiàn)在調(diào)用者的代碼里。無法實現(xiàn)二者之間的松耦合。
第二種情況下,調(diào)用者無須關(guān)心被調(diào)用者具體實現(xiàn)過程,只需要找到符合某種標準(接口)的實例,即可使用。此時調(diào)用的代碼面向接口編程,可以讓調(diào)用者和被調(diào)用者解耦,這也是工廠模式大量使用的原因。但調(diào)用者需要自己定位工廠,調(diào)用者與特定工廠耦合在一起。
第三種情況下,調(diào)用者無須自己定位工廠,程序運行到需要被調(diào)用者時,依賴注入容器自動提供被調(diào)用者實例。事實上,調(diào)用者和被調(diào)用者都處于依賴注入容器的管理下,二者之間的依賴關(guān)系由依賴注入容器提供。因此調(diào)用者與被調(diào)用者的耦合度進一步降低,這使得應(yīng)用更加容易維護,這就是依賴注入所要達到的目的。
在PHP中如何實現(xiàn)依賴注入呢?
依賴注入在現(xiàn)在的編程中,有著比較突出的優(yōu)勢,這也是ThinkPHP、Laravel等框架,使用此編程思想的原因。那么這些框架到底是如何使用PHP去實現(xiàn)的呢?
下面我們使用一個簡單的案例,來說說其中的奧秘。
首先我們創(chuàng)建一個類,看起來是這樣的,包含一個屬性和兩個方:

假設(shè)我們現(xiàn)在有另外兩個類,redisDB和cache,redisDB提供一個redis數(shù)據(jù)庫的操作,cache負責緩存功能的實現(xiàn)并且依賴于redisDB。如下:

在上面這個類中我們簡單實現(xiàn)了redis的查詢、保存和刪除。另一個類和當前這個類結(jié)構(gòu)很像:



現(xiàn)在我們就當已經(jīng)實現(xiàn)了redisDB和cache這兩個組件,具體的細節(jié)這里就先不做討論了,來看看如何使用使用吧。首先需要將兩個組件注入到容器中:

到這里你可能會覺得這樣以來反而有點繁瑣了。cache和redisDB的結(jié)構(gòu)如此之像,完全可以把redis寫到cache中而沒必要單獨分離出來?但是你想過沒有,有些數(shù)據(jù)及時性沒那么高而且數(shù)量比較大,用redis有點不合適,mongodb是更好的選擇;有些數(shù)據(jù)更新頻率更慢,對查詢速度也沒要求,直接寫入文件保存到硬盤可能更為合適;再或者,你的客戶覺得redis運維難度有點大,讓你給他換成memcache... 這就是為什么把它分離出來了。然后,繼續(xù)改進代碼:



我們新增加了一個接口BackendInterface,規(guī)定了redisDB,mongoDB,file這三個類必須實現(xiàn)這個接口所要求的功能,至于其他錦上添花的功能,隨你怎么發(fā)揮。而cache的代碼,好像沒有變,因為cache不需要關(guān)心數(shù)據(jù)是怎么存入數(shù)據(jù)庫或者文件中。而cache的調(diào)用者,也不需要關(guān)心cache具體是怎么實現(xiàn)的,只要根據(jù)接口實現(xiàn)相應(yīng)的方法就行了。多人協(xié)作你會更加受益,你們只需要商定好接口,然后分別實現(xiàn)就行了。
以上代碼還可以繼續(xù)改進,直到你認為無可挑剔為止。比如,redis服務(wù)在一個請求中可能會調(diào)用多次,而每次調(diào)用都會重新創(chuàng)建,這將有損性能。只需擴展一下DI容器就好增加一個參數(shù)或增加一個方法,隨你。

這樣以來,如果某個服務(wù)在一次請求中要調(diào)用多次,你就可以將shared屬性設(shè)置為true,以減少不必要的浪費。如果你覺得每次在注入時都要setDi有點繁瑣,想讓他自動setDi,那可以這么做:

然后,就可以這樣:

我們現(xiàn)在所實現(xiàn)的這個DI容器還很簡陋,還不支持復(fù)雜的注入,你可以繼續(xù)完善它。不過,通過這些代碼你已經(jīng)了解什么是依賴在注入了,你可以將這種思想應(yīng)用到你的項目中,或者著手開發(fā)你自己的框架。
注:文章內(nèi)容和圖片均來源于網(wǎng)絡(luò),只起到信息的傳遞,不是用于商業(yè),如有侵權(quán)請聯(lián)系刪除!


