依賴反轉原則
定義
High level modules should not depend upon low level modules.
Both should depend upon abstractions.
Abstractions should not depend upon details. Details should depend upon abstractions.
說明
- 高層次的模組不應該依賴於低層次的模組,兩者都應該依賴於抽象介面。
- 抽象介面不應該依賴於具體實現。而具體實現則應該依賴於抽象介面。
實作建議
- 每個類別都需要有介面或抽象類別
- 使用的變數型別應該盡量是抽象而不是具體類別。
- 盡量不要從具體類別繼承。
當然實務上沒有對的情境,但如果已經知道繼承會導致一些衍伸的問題,那在做這個決定之前,更應該好好思考一下,是否能夠用繼承介面來解決,又或者是有其他的做法可以處理。
- 結合里氏替換原則的內容,可以得出一個大致的規範。
介面:負責定義 public 的方法和屬性 抽象類:負責定義建構函式部分的實現
實例
需求
// ...
設計
// ...
效益 & 注意事項
效益
- 當我們依賴抽象 (介面/抽象類別) 而不是具體實現 (類別),可以透過一些單元測試的工具,很快做出假物件來對我們的功能做驗證。
對於開發人員來說,不需要一開始就煩惱具體的實作是甚麼,而是可以更快地透過介面來驗證我們的功能設計是不是我們想要的。甚至可以在具體實現完成之前,對我們的功能進行驗證。(TDD = Test-Driven Development = 測試驅動開發) - 抽象本身決定了要如何使用這個功能,只要這個介面是被確定的,不管具體實現是誰做的,如何做的,都不會跳脫出這個範疇,使用的人再也不用擔心因為具體實現類別變更,導致所有用到這個功能的地方都需要重新檢查有沒有被影響到。
注意事項
- 在小型項目中很難體會依賴反轉的優點,相較於使用介面,直接 new 出一個需要的物件更快更實際。
只有當專案越來越大,參與的人員越來越多,才能感受到當需求不斷變更的狀況下,只有使用依賴反轉才能讓程式的穩健度提高,讓維運人員可以更加的容易地調整和擴展。 - 依賴反轉後,因為依賴的是抽象,那接下來的問題就是,應該透過甚麼機制,把真正的實體傳入,甚至應該要如何控制這個物件的生命週期,是開發人員必須要額外去 Survey 的內容。(ex: DI 框架)