From Apprentice To Artisan 一書個人理解

twisted-fate發表於2017-07-24

From Apprentice To Artisan 原文翻譯線上觀看

在讀這本書之前其實是沒怎麼看過Laravel的原始碼的,官方文件雖然有寫,卻感覺到了枯燥,所以其實對依賴注入,IoC容器,服務提供者的理解還是很淺顯,即使知道了他們的原理卻不知道該怎麼運用。讀完下來跟著作者一起看原始碼,寫實現,擴充套件,才把之前的知識空缺給補充上,才知道Laravel的原始碼簡直就是一本框架實現原理教科書,同時美輪美奐的註釋和結構又區別於教科書的枯燥,強烈推薦大家看看。接下來說說讀完之後對書中提到概念的理解,也當作一遍複習,有理解錯誤的地方希望各位指出。

依賴注入

第一次接觸Laravel的時候,我也被依賴注入這個名詞搞的頭暈腦脹,查詢了許多相關資料,發現有許多喜歡和IOC容器的概念混在一起講。但其實它們解決的問題是不一樣的。依賴注入模式(Dependency Injection ,簡稱DI,也成為控制反轉模式,簡稱IOC)。

依賴注入主要解決的問題是:早期通過工廠模式解耦例項化過程,但當依賴的物件越來越龐大的時候,工廠也變得難以維護,依賴注入將物件的例項化從內部轉到外部,但依舊是手動將依賴注入。

IOC容器解決的問題:將手動依賴注入變為自動依賴注入,通過反射實現。

在依賴注入這一章作者首先說明了依賴注入和IoC容器的關係,因為初學的時候這兩個概念很容易混在一起搞不清楚,關鍵就是實現依賴注入並不需要IoC容器,因此在學習的時候切記不要混淆了概念而使得學習事倍功半。

為什麼

然後是為什麼需要使用依賴注入?主要是將職責分離到不同的類中(解耦),當使用了介面在注入時進行型別宣告的時候,可以輕易切換實現類,並且測試起來也更方便(使用Mockery模仿庫)。

如何實現

如何實現依賴注入,就只是將不屬於類1職責的部分挪出來,新建一個負責這部分職責的類2,將這個類2的例項“注入”到類1中,由類1按需呼叫。

更進一步

本章第三節講了一個例子鞏固理解,提到了Contract約定(其實就是介面)。寫介面雖然麻煩但是它帶來的好處有很多:可以輕易切換實現,可以不需要具體實現,通過模擬進行快速測試。似乎介面和依賴注入帶來的好處很像,其實就是因為依賴注入將耦合的程式碼分離成了介面,間接獲得了介面的功能。

第四節講了開發時要靈活變通,不必死死遵循各種原則,小專案不必使用介面也可以運作的很好,收益不高。大專案使用介面的收益會很大。

IoC容器

開篇先講了容器主要作用是來管理依賴注入,容器是Laravel的核心,Application類就是繼承自Container類。

容器具體解決什麼問題?一是通過App::bind繫結介面的實現,可以輕易地切換介面的具體實現,所以容器就是一個用來儲存各種繫結的地方。singletoninstance區別不大。Laravel的容器類甚至可以抽離出來單獨使用。

二是Laravel容器使用了PHP“反射”的機制這個強大的特性,反射是一種執行時探測類和方法的能力。

反射的例子

在作者的例子中UserController依賴於StripBiller 。當StripBiller 沒有在容器中繫結的時候,反射機制就會發揮作用,先探測StripBiller 所需要的依賴,然後遞迴解決,最後返回StripBiller的例項。

有了反射,我們可以免去寫大量繫結的煩惱。但是當使用介面進行依賴型別宣告的時候,我們需要將具體的類繫結到介面上,因為介面是無法被例項化的。

最後,若想深入,請讀Illuminate\Container\Container原始碼 (還沒讀)。

第三章主要講的是介面,強型別弱型別的知識。

前面提到的容器需要繫結具體實現類,而服務提供者就是負責繫結的地方。在app/config/app.php中可以檢視所有的服務提供者。

我們在register方法中寫相關的繫結,因為在

“程式框架剛啟動時,所有在你配置檔案裡的服務提供者的 register 方法就會被呼叫”

另外不要在register中使用任何服務,這個方法只用來繫結。

“所有關於繫結類後續的判斷、互動都要在 boot 方法裡進行”

服務提供者並不是必須的,因為服務提供者只是一個用來自動初始化服務元件的地方,一個方便管理引導程式碼和容器繫結的地方。並不是你非要釋出個什麼軟體包才需要服務提供者,他們只是非常好的管理程式碼的工具。使用它們的力量去管理好應用中的各個元件吧。

服務提供者是延遲載入的。

服務提供者還有另一個方法boot,這個方法在所有register方法執行完畢之後觸發,在啟動方法裡面,你想做什麼都可以。

最後,作者建議閱讀各種Providers的原始碼,如FilesystemServiceProviderExceptionServiceProvider

”有人會說核心服務提供者和應用程式容器就是 Laravel 。Laravel 其實是將這麼多不同部分聯絡起來,形成一個單一的、內聚的整體的這麼一個機制。拿建築來比喻,那些服務提供者就是框架的預製模組。”

作者的例子舉的很貼切,不時提醒我們容易忽略的細節,同時不要被條條框框限制住,嗯,畢竟是Laravel框架的Creator。後面還有幾章對框架的深入使用,幾章關於SOLID原則。

之前有看了不少各種各樣的關於SOLID的講解,教學。而自己真正理解了的可能只有單一職責原則,其他幾個的概念基本處於模糊不清的狀態。看了這部分之後,終於加深了對它們的理解,而核心是knowledge知識(後來才知道是迪米特原則),即用一個類需不需要懂得這種知識來區分邊界,而這些原則的目的就是達成這一目標。當一個類知道的太多的時候,它就打破了某些原則。

5個原則緊密相連,如果其中一個原則沒有被遵循,那麼其他大部分(可能不會是全部)的原則也會出問題。

Single Responsibility(單一職責):5個原則中最好理解的原則,即一個類只負責他職責範圍內的事,一個類不需要知道與他職責無關的。
典型例子:控制器只需要負責接收和轉發請求,不需要知道如何處理請求,處理請求可通過依賴注入請求處理類進行操作


Open Closed(開閉原則):易於擴充套件,對修改關閉。
典型例子:複合搜尋的Filters類,不再用if判斷是否有該搜尋條件,然後新增where語句,而是使用模板模式。Github示例程式碼


Liskov Substitution(里氏替換):如果一個類使用了一個介面的一個實現類,那麼該介面的任何其他實現類也可以被這裡直接使用,不用做出任何修改。

典型例子:Laravel中的Contract,在應用中使用介面作型別提示,在容器中將特定實現類繫結介面,可自由替換。


Interface Segregation(介面隔離):介面隔離原則規定在實現介面的時候,不能強迫去實現沒有用處的方法。你是否曾被迫去實現一些介面裡你用不到的方法?如果答案是肯定的,那你可能建立了一個空方法放在那裡。被迫去實現用不到的函式,這就是一個違背了介面隔離原則的例子。

典型例子:這個原則在PHP的官方session實現中被違反,可能是5個例子中重要性較小的一個。(或者是我水平不夠還沒達到這個層次)


Dependency Inversion(依賴倒置):該原則要求高等級程式碼不應該依賴低等級程式碼,抽象定義不應該依賴具體實現。

低階程式碼用於實現基本的操作,比如從磁碟讀檔案,運算元據庫等。高階程式碼用於封裝複雜的邏輯,它們依靠低階程式碼來達到功能目的,但不能直接和低階程式碼耦合在一起。

典型例子
1.認證使用者類,需要查詢使用者資料庫表,中間抽象出一層使用者操作類, 使得認證使用者類不依賴於資料庫類,可輕易改變資料庫類的實現
2.工廠模式,高等級程式碼不直接依賴於低等級的實現,而是依賴於工廠。

Java中的另外幾個設計原則:迪米特原則(最少知道原則,knowledge劃分界限),合成複用原則(儘量使用組合,而不是繼承)。


  • 最後,Show respect to Taylor Otwell
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章