讀書心得-依賴注入:原理、實作與設計模式

前言

基本上我的讀書心得大概都是看到哪裡寫到哪裡,也很有可能看到一半就忘了這件事情

  1. 依賴注入 原理、實作與設計模式

Source Code download 可於上述網址中找到連結

PART 1 朝著依賴注入邁進

CH1 依賴注入的基礎:定義、理由與實作方法

  1. 破解對依賴注入的常見誤解
  2. 為何要實作依賴注入
  3. 依賴注入帶來了哪些好處
  4. 何時需要依賴注入

這一章節的學習重點如上,主要是定義了一些名詞,並闡述了一些關於依賴注入的正確觀念,了解這一章的東西我覺得最重要的事情是:獲得正確的概念,讓你在瀏覽別人文章、或是查詢相關知識,都有一個正確的切入點;而對於剛接觸依賴注入的人,也透過一些簡單的例子,試圖用這些日常生活中常見的東西,解釋一些設計模式,並且將依賴注入的觀念帶進來

我在這一個章節中補充了一些以往不足的基礎觀念;雖然不是這章節的重點,但這本書引用別本書的重點:制式回應(Stub)、模擬器(Mock)或是簡化替身(Fake)等等…這些反而讓我先前很容易混淆的名詞、概念,有更簡單的記憶方法。

CH2 緊耦合架構

  1. 先編寫一份緊耦合架構的應用程式
  2. 評估該應用程式的可重組性程度
  3. 分析該應用程式的組合性缺陷問題

這章節透過情境,引導我們從需求到程式碼,一步一步的見證在每個階段的程式碼,最後,再回過頭來審視這份 Code 有甚麼問題,不得不說,後半段很精采

有一些東西是已經知道,還有一些東西是沒想過的觀念及問題,他提出的問題包含了幾個面向,並解釋原因:晚期繫結、可擴展性、可維護性、平行開發、可測試性。我覺得再多看幾次這種東西,能夠大幅提升自己Code Review的功力吧,在 CH2 再次提到了幾次不穩定依賴,這個在 CH1 介紹過的名詞,有著他自己的定義,如果不熟悉,最好還是回去溫習一下;而這個章節最後也再次提醒,盡量避免對不穩定依賴的對象緊耦合在一起

CH3 鬆耦合架構

  1. 以鬆耦合架構重新打造 Mary 的電商網站應用程式
  2. 評估鬆耦合架構應用程式
  3. 對鬆耦合應用程式的剖析

相較於 CH2,這一章示範了相同的需求,以YAGNI的精神從前端開始,所以一步步的開發都是依據需求,有點TDD的概念,接著一直到Controller裡面先做個假的資料,並讓客戶確認是否合乎需求

關於這個觀念,其實可以發現近年來很多人都提倡這樣的方式在專案中,避免最終開發誠品與客戶需求有落差;不管是前端、後端,都可以運用這樣的方式快速取得回饋。說得明白一點就是我先趕緊弄一版出來,給你看看是不是你要的,如果不行,告訴我哪邊有問題,我趕緊修給你。

在這一章節提到了組合根(Composition Root),其實就是在程式的最外層,負責建立各個實體類別的地方,以 MVC 網站來說是controller;以Console app來說是Main()之內,在後續的程式碼需要使用的話,就透過方法注入,一層層的給進去

這章節的範例程式又呼應了CH1提到的重點:針對介面設計程式,而非實作,後面的部分都是撰寫程式的一些細節,對於不是很理解的人也可以選擇下載對應的程式碼直接看,這也是我很喜歡的一個部分,因為下載下來的程式碼,他真的可以執行,甚麼都不用在設定,對於需要這份程式碼來學習的人是很有幫助的

就是不會才要看 Code 怎麼實作的,如果連跑都跑不起來,還需要相關知識才能夠排除障礙,就失去了sample code的意義了吧
CH3 範例位於DIPPP_source-code\MarysECommerce

下載 Code 執行還有個好處,原本書中的範例我看的總是有點彆扭,因為不符合自己的開發習慣,以下面這個例子來看,我花了很久的時間還是抓不到脈絡

1
2
3
4
5
6
7
8
9
10
11
public ViewResult Index()
{
IEnumerable<DiscountedProduct> featuredProducts =
this.productService.GetFeaturedProducts();

var vm = new FeaturedProductsViewModel(
from product in featuredProducts
select new ProductViewModel(product));

return this.View(vm);
}

改成下面這樣之後,秒懂,所以code style還是很重要的

1
2
3
4
5
6
7
public ViewResult Index()
{
IEnumerable<DiscountedProduct> featuredProducts = this.productService.GetFeaturedProducts();
IEnumerable<ProductViewModel> products = featuredProducts.Select(product => new ProductViewModel(product));
FeaturedProductsViewModel vm = new FeaturedProductsViewModel(products);
return View(vm);
}

PART 2 分門別類

CH4 DI 設計模式

  1. 利用組合根來組合物件關聯
  2. 利用建構子注入來靜態定義對依賴關係的需求
  3. 利用方法注入在組合根之外的地方滿足依賴關係
  4. 利用屬性注入來定義額外的依賴需求
  5. 如何選擇設計模式

從這一章節開始會將一些名詞定義出來,並針對這些東西做一個比較深入的解釋,算是非常重要的基本功吧;而且每個部分都講得蠻詳細的,將可能犯的錯誤,為什麼這樣是不好的,以及應該怎麼處理都有提供範例解釋,只是要耐著性子仔細閱讀。這邊的選擇設計模式,其實就是教你如何在各種情境下,選擇注入方式的判斷依據

採用建構子注入的建構子,內容應該只有防衛性語句、保存依賴對象這兩種作業
如果有一些依賴是可有可無的,最好可以在組合根提供 null object,避免在程式中判斷這些可有可無的依賴關係而增加複雜度

這一章如果沒有抓到重點,直接看它的小結吧,字字珠璣

CH5 DI 反模式

  1. 造成程式碼緊耦合的控制狂
  2. 索要依賴對象的服務定位
  3. 把不穩定依賴搞的到處都是的環境物件
  4. 非得用某種識別定義建構子的限制性建構

這章節提出了四種不是很好,卻又常見的錯誤,單單看名詞是很難理解的,而這個章節會將各種不好的做法都給予範例解釋,而且範例其實都很簡單,原本以為這些東西很難理解,因為用文字解釋的確是很難懂;用程式碼解釋搭配一些簡單的敘述,反而很好理解;但是可能翻譯的關係,一些名詞還是要自己在心裡替換掉才能看懂,總體來說,這一章節雖然有料,也能看懂,但其實後面的部分因為翻譯以及充斥著 IT 的專業名詞,可能會需要多看幾次才能稍微理解,如果真的看不懂或是沒耐心看,我是覺得可以略過

第五章介紹的這幾種anti pattern,實際上都可以透過第四章的某種注入方式取代

CH6 程式異樣

  1. 如何處理建構子過度注入的程式異樣
  2. 辨認並預防抽象工廠模式的濫用
  3. 修正循環依賴的程式異樣

我個人是把程式異樣(code smell)程式碼壞味道(bad smell)當作是同樣的事情,意思是程式碼可能有一些問題,或者是可能未來會發生哪些問題的前兆,這個徵兆就是bad smell

建構子過度注入

第一個部分在解釋建構子過度注入的問題以及解決方案,可以透過facade pattern或是domain event的方式重構,解說的很清楚,圖也畫的很容易理解,但是domain event的概念還沒有很懂,很棒的是,它的範例經過設計,與facade pattern重構結果比對來看,程式碼都是一樣的,就只是依賴的介面不同,兩者交互著看,就比較容易理解,可能要多看幾次才會比較好內化,衝這一波不虧,找時間多看吧

抽象工廠的濫用

第二個部分老實說我完全看不懂它中文在說甚麼,直到它給出了錯誤與正確的程式碼範例,其實這也是我在看這種字很多的書的實際閱讀過程:看字看不懂,解釋聽不懂,直接看 Source 比對,哪些是不好的?哪些是好的?差異為何?回過頭去看文字說明,看它是怎麼解釋程式碼的差異部分,接著,如何歸納成自己能理解的文字。再重新看一次書中怎麼描述這件事情的,這會讓我下一次看到這些東西能夠更好的理解成我自己的文字

書中有一些解釋落於文字,就勢必要寫的很饒舌,很官方;但其實意思用口語說出來,反而很容易理解

看這一章開始懷疑之前所學的東西,一些我們常在用的開發習慣居然都是bad smell,但其實就跟重構一樣,很多時候它只是一種選擇,只是當時的最佳解,不代表所有的專案都可以這樣子用,具體的還是要看時空環境,學到這些東西,至少讓我再下次遇到的時候,有多一種選擇可以用。

循環依賴

這一段的程式碼跟故事有點多,其實我直接看圖而已,循環依賴是我已經理解的東西,所以我在這一部分就略過的比較多,不過如果對於這點不清楚的人,動手照著做一遍範例,應該是能夠理解問題癥結點的,不過後續的幾種解決方案倒是整理的不錯

這一章對於重構的學習有很大幫助,如果可能,我希望這類型的書多出一點

PART 3 單純 DI 架構

CH7 應用程式的物件組合

書中為了解釋在應用程式裡面,如何將物件組合起來,特別舉了三個例子

  1. 指令列介面應用程式的物件組合
  2. 通用 Windows 平台應用程式的物件組合
  3. ASP.NET 核心 MVC 框架應用程式的物件組合

但是實際上我沒有再寫 UWP,所以是直接略過,只看第一個 Console App 還有最後的.net core 部分;在這兩個部分他都是針對應用程式的起始點開始作為物件組合的地方,在 Console App 裡面想當然就是 program 裡面的 main 方法;而.net core 下,起始點則是在裡面的 main 方法;而 MVC 框架下,起始點則是在 Startup.cs 下。關於這一章節的說明我有點看不懂,反覆看了幾次還是一樣,大概只知道在.net core 下面如何將物件組合起來,反正書中有範例照著做就行了;但也因為目前沒有碰過.net core 實務開發,很多東西又跟 asp.net mvc 不同,所以其實沒什麼感覺也沒什麼想法;我只知道如果未來我想溫習一下.net core 的物件組合方式,可以回來參考一下這一章節,至於 asp.net mvc,希望後面章節會有解決方案,要不然我還是要去 Google 查

CH8 物件生命週期

  1. 管理依賴對象的生命週期
  2. 處理依賴的廢棄問題
  3. 生命週期型態彙整
  4. 不良生命週期型態

在吸收這部分的知識之前,要對scope(作用域作用域)Garbage Collection(記憶體回收的基本概念)Reference要有一點基本認識,就能看懂書裡面的解釋。接著就是探討被注入的物件,是共用還是各自獨立?這些都會影響到被注入的物件的生命週期;各自獨立就只需要使用端離開scope就可以回收;共用則是需要等到所有使用端都離開scope才會回收。

但是書裡面會用一些比喻來解釋這件事情,我真心覺得難以理解,可能有人覺得調酒師、餐廳用餐的例子很好懂吧,但我卻是越看越模糊,最後是根本不看他的這些比喻,直接跳過,看他直接解釋技術還比較不容易混淆。

在解釋完生命週期這件事情後,接著要探討的是在單純DI架構下管理生命週期這件事情,這裡用上一章節的code來解釋一次性的生命週期,所以我又回去翻了第七章,其實也很好懂,而且在CH8也為這個範例做了一個物件關聯圖,比對書裡面的解釋更容易理解;這裡有一個重點就是無狀態的類別,可以考慮用singleton來實作;並且在後續的章節也說明了為何在.Net裡面我們通常不自己實作IDisposable介面,但是如果是開發WCF,需要注意的東西就不太一樣了。

接著後面如何處理依賴的廢棄問題,其實重點就是在組合根的地方來觸發物件關聯的建立與釋放。細節還有anti pattern在書裏面都有提,但我沒看懂,就光記結論了
然後就是介紹一下生命週期型態,也就是單例型態一次性型態作用域型態這三種,然後給出了一些重點及範例;並且在這章節的最後也介紹了不良的生命週期型態,這幾種都有給出範例。

CH9 介入攔截

TBD

CH10 設計面上的剖面導向程式設計

TBD

CH11 以工具實作的剖面導向程式設計

TBD

PART 4 DI 容器技術

CH12 DI 容器

TBD

CH13 DI 容器介紹:Autofac

TBD

CH14 DI 容器介紹:Simple Injector

TBD

CH15 DI 容器介紹:Microsoft.Extensions.DependencyInjection

TBD