如何理解Laravel 的IoC容器

Home / Paper / 如何理解Laravel 的IoC容器

學習laravel快小一年了,到現在才去研究laravel 的核心’容器IOC’ 這些概念. 寫項目的時候有大概看看關於IOC 文章, 但是沒有深入理解,只是有個概念,趕著寫代碼,雖然代碼也寫的很菜· – ·

這幾天花了點時間研究了一下laravel 的核心’服務容器’ 然後理解了一下關於IOC 的概念. 不敢說百分百掌握了,但是比之前是有一定加深. 所以決定把自己理解的分享一下, 把自己的第一次博文獻給laravel. 理解不到位的還請各位大牛多多指正.

1.依賴

IOC( inversion of controller )叫做控制反轉模式,也可以稱為(dependency injection ) 依賴注入模式。要理解依賴注入的概念我們先理解下什麼依賴

通過上面的代碼我們知道,當我們創建一個class PayBill 的實例的時候, PayBill 的構造函數里面有{ $this->payMethod= new Alipay (); }, 也就是實例化了一個class Alipay . 這個時候依賴就產生了, 這裡可以理解為當我想用支付寶支付的時候, 那我首先要獲取到一個支付寶的實例,或者理解為獲取支付寶的功能支持. 當用我們完new 關鍵字的時候, 依賴其實已經解決了,因為我們獲取了Alipay 的實例.

其實在我知道ioc概念之前,我的代碼中大部分都是這種模式~ _ ~ . 這種有什麼問題呢, 簡單來說, 比如當我想用的不是支付寶而是微信的時候怎麼辦,你能做的就是修改Payment 的構造函數的代碼,實例化一個微信支付Wechatpay.

如果我們的程序不是很大的時候可能還感覺不出什麼,但是當你的代碼非常複雜,龐大的時候,如果我們的需求經常改變,那麼修改代碼就變的非常麻煩了。所以ioc 的思想就是不要在class Payment 裡面用new 的方式去實例化解決依賴, 而且轉為由外部來負責,簡單一點就是內部沒有new 的這個步驟,通過依賴注入的方式同樣的能獲取到支付的實例.

2.依賴注入

依賴我們知道了是什麼意思,那依賴注入又是什麼意思呢,我們把上面的代碼拓展一下

上面的代碼中,跟之前的比較的話,我們加入一個Pay 接口, 然後所有的支付方式都繼承了這個接口並且實現了pay 這個功能. 可能大家會問為什麼要用接口,這個我們稍後會講到.

當我們實例化PayBill的之前, 我們首先是實例化了一個Alipay,這個步驟就是生成了依賴了,然後我們需要把這個依賴注入到PayBill 的實例當中,通過代碼我們可以看到{ $pb = new PayBill ( payMethod ); }, 我們是通過了構造函數把這個依賴注入了PayBill 裡面. 這樣一來$pb 這個PayBill 的實例就有了支付寶支付的能力了.

把class Alipay 的實例通過constructor注入的方式去實例化一個class PayBill. 在這裡我們的注入是手動注入, 不是自動的. 而Laravel 框架實現則是自動注入.

3.反射

在介紹IOC 的容器之前我們先來理解下反射的概念(reflection),因為IOC 容器也是要通過反射來實現的.從網上抄了一段來解釋反射是什麼意思

“反射它指在PHP運行狀態中,擴展分析PHP程序,導出或提取出關於類、方法、屬性、參數等的詳細信息,包括註釋。這種動態獲取的信息以及動態調用對象的方法的功能稱為反射API。反射是操縱面向對象範型中元模型的API,其功能十分強大,可幫助我們構建複雜,可擴展的應用。其用途如:自動加載插件,自動生成文檔,甚至可用來擴充PHP語言”

舉個簡單的例子

dump 的得到的$constructor 和$dependencies 結果如下

通過上面的代碼我們可以獲取到class A 的構造函數,還有構造函數依賴的類,這個地方我們依賴一個名字為’args’ 的量,而且通過TypeHint可以知道他是類型為Class B; 反射機制可以讓我去解析一個類,能過獲取一個類裡面的屬性,方法,構造函數, 構造函數需要的參數。有個了這個才能實現Laravel 的IOC 容器.

4.IOC容器

接下來介紹一下Laravel 的IOC服務容器概念. 在laravel框架中, 服務容器是整個laravel的核心,它提供了整個系統功能及服務的配置, 調用. 容器按照字面上的理解就是裝東西的東西,比如冰箱, 當我們需要冰箱裡面的東西的時候直接從裡面拿就行了. 服務容器也可以這樣理解, 當程序開始運行的時候,我們把我們需要的一些服務放到或者註冊到(bind)到容器裡面,當我需要的時候直接取出來(make)就行了. 上面提到的bind 和make 就是註冊和取出的兩個動作.

5. IOC 容器代碼

好了,說了這麼多,下面要上一段容器的代碼了. 下面這段代碼不是laravel 的源碼, 而是來自一本書《laravel 框架關鍵技術解析》. 這段代碼很好的還原了laravel 的服務容器的核心思想. 代碼有點長, 小伙伴們要耐心看. 當然小伙伴完全可以試著運行一下這段代碼,然後調試一下,這樣會更有助於理解.

上面的代碼就生成了一個容器,下面是如何使用容器

當我們實例化一個Container得到$app 後, 我們就可以向其中填充東西了

當執行完這兩行綁定碼後, $app 裡面的屬性$bindings 就已經有了array 值,是啥樣的呢,我們來看下

當執行$paybill = $app->make(“tryToPayMyBill”); 的時候, 程序就會用make方法通過閉包函數的回調開始解析了.

解析’tryToPayBill’ 這個字符串, 程序通過閉包函數和build方法會得到’PayBill’ 這個字符串,該字符串保存在$concrete 上. 這個是第一步. 然後程序還會以類似於遞歸方式將$concrete 傳入build() 方法. 這個時候build裡面就獲取了$concrete = ‘PayBill’. 這個時候反射就派上了用場, 大家有沒有發現,PayBill 不就是class PayBill 嗎? 然後在通過反射的方法ReflectionClass(‘PayBill’) 獲取PayBill 的實例. 之後通過getConstructor(),和getParameters() 等方法知道了Class PayBill 和接口Pay 存在依賴

接著,我們知道了有’Pay’這個依賴之後呢,我們要做的就是解決這個依賴,通過getDependencies($parameters), 和resolveClass(ReflectionParameter $parameter) ,還有之前的綁定$app->bind( “Pay”, “Alipay”); 在build 一次的時候,通過return new $concrete;到這裡我們得到了這個Alipay 的實例

到這裡我們總算結局了這個依賴, 這個依賴的結果就是實例化了一個Alipay. 到這裡還沒結束

上面的$instances 數組只有一個element 那就是Alipay 實例

最終通過newInstanceArgs() 方法, 我們獲取到了PayBill 的實例。

到這裡整個流程就結束了, 我們通過bind 方式綁定了一些依賴關係, 然後通過make 方法獲取到到我們想要的實例. 在make中有牽扯到了閉包函數,反射等概念.

好了,當我們把容器的概念理解了之後,我們就可以理解下為什麼要用接口這個問題了. 如果說我不想用支付寶支付,我要用微信支付怎麼辦,too easy.

是不是很簡單呢, 只要把綁定從’Alipay’ 改成’Wechatpay’ 就行了,其他的都不用改. 這就是為什麼我們要用接口. 只要你的支付方式繼承了pay 這個接口,並且實現pay這個方法,我們就能夠通過綁定正常的使用. 這樣我們的程序就非常容易被拓展,因為以後可能會出現成百上千種的支付方式.

好了,到這裡不知道小伙伴有沒有理解呢,我建議大家可​​以試著運行下這些代碼, 這樣理解起來會更快.同時推薦大家去看看《laravel 框架關鍵技術解析》這本書,寫的還是不錯的.


轉載請註明:作者[[哎喲我的巴扎黑]
自由轉載-非商用-非衍生-保持署名(創意共享3.0許可證
原文網址:https://laravel-china.org/articles/4076

721 全部 2 今日

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *

*