什麼是依賴反轉原則 (DIP)?
依賴反轉原則 (Dependency Inversion Principle, DIP) 是物件導向設計 SOLID 原則中的第五項,核心思想在於:高層模組不應依賴低層模組,兩者都應依賴於「抽象」。此外,抽象不應依賴細節,而是細節應依賴抽象。在實務開發中,這意味著我們應該透過定義「介面 (Interface)」或「抽象類別」來規範模組間的互動,而非直接呼叫具體的實作類別。透過這種反轉,當低層實作變動時(如更換資料庫或第三方套件),高層業務邏輯無需修改,從而達成系統的高度解耦、提升可測試性與長期維護性。
在開始之前先來首歌吧,覺得熟悉的話,你就暴露年紀了 >.^
來談談物品的管理。生而為人,我們的目的是使用物品,而不是被物品牽制而影響。這就是 依賴反轉原則 (DIP) 在生活中的體現:將物品的安排方式適應生活需求,而非讓生活被動適應物品。
當你不去整理身邊的物品,那生活最終就會不得不依賴物品的變化進而被影響原本的生活。
- 當我們的衣服、鞋子越買越多,受限於空間我們只能不停的堆放,甚至有些會被放在深處不再拿出,接下來找尋特定衣物就會漸漸變得困難。
- 當今天工作相關紙本堆積如山,包括文件、發票、收據,如果沒有依照需求進行分類整理和規範,最終就會難以保持整齊的工作環境。
在生活上依賴於物品的安排和存放方式,而不是將物品的安排和存放方式適應生活需求,這種方式通常會導致物品難以找到、使用不方便,甚至造成浪費。如果所有事情都放在心上,那根本沒辦法放鬆和休息,那我們可以怎麼辦?
役物,而不役於物,君子寡欲則不役於物,可以直道而行
很多時候並不是時間不夠,而是不了解任務的定義和性質,不了解的情況下當然也沒辦法有良好的規範去遵循。
Dependency Inversion Principle (DIP):解除模組間的強耦合
來舉一個工作上常見的案例。當你越來越資深的時候,工作上肯定會遇到 依賴反轉原則 的應用情境:
- 資深工程師 -> 菜鳥工程師弟弟妹妹
- 資深工程師 -> 菜鳥實習生弟弟妹妹
隨著工作上遇到的弟弟妹妹越來越多,你可能會漸漸的忘記弟弟妹妹們的職能、技能和擅長的事物,一不小心也有可能記不起來名字,這時候在合作上就容易出現問題。
- 資深工程師 -> 很多弟弟妹妹
這個時候我們就需要定義一個有相關規範的角色給大家遵守,這時候人與人之間關係就會變成:
- 資深工程師 -> 菜鳥工程師 <- 弟弟妹妹
- 資深工程師 -> 菜鳥實習生 <- 弟弟妹妹
最大的差異在多定義一層 菜鳥工程師 和 菜鳥實習生 來規範該職能所需的技能和可以完成的工作。這樣資深工程師就只需要針對 菜鳥工程師、菜鳥實習生 做對應的引導,而對弟弟妹妹來說也會明確知道身為菜鳥工程師或是菜鳥實習生該做什麼才會符合那個角色的定義。
當你不小心忘記實習生名字的時候,你就直接叫「實習生大大」就不會叫錯了 (O)
透過依賴的反轉,我們可以不再被牽制,而是真的在讓彼此的生活過得更好。
API 契約:確保系統穩定性的關鍵協議
當我們今天需要別人提供服務的時候,我們就會需要導入 Application Programming Interface (API) 的概念。API 設計 其中一個核心價值就是「契約」。
當這個協議定下來後,就不能輕易改變,其中包含:
- 功能定義:支援的操作、輸入參數、輸出格式。
- 授權和認證:透過 API 金鑰、OAuth 令牌來控制訪問權限。
- 使用限制:如每分鐘的請求數限制、資料傳輸流量。
- 故障處理:定義錯誤碼與建議的解決方法。
- 版本控制:確保使用者知道自己使用的是哪個版本。
當這個契約和規範成立之後,提供 API 的人會依照規範進行實作,而使用 API 的人也同樣依照規範進行調用。由於 API 的出現,使用者就不再依賴具體的服務提供者。這就是 控制反轉 (IoC) 的核心思想:將控制權交給規範與介面。只要 API 契約沒變,服務提供者即使從人類換成機器人也不影響結果。
- 使用者 -> API (抽象契約) <- 服務提供者
依賴反轉與斷捨離:追求軟體與生活的極致簡約
也許真正的富足並不來自於擁有多少物品,而是來自於對生活的滿足和平靜。人生真正有價值的只有時間,當我們減少對物品的需求,也許就能有更多的時間和精力來關注更重要的事情。
「依賴反轉」的習慣,不僅能協助我們優化程式架構,更能協助我們在生活中找到一種平衡,對於那些擁有感到足夠,同時保持心靈的寧靜。
FAQ:依賴反轉原則常見問題
Q1:DIP 與 IoC (控制反轉) 的關係是什麼?
A:DIP (依賴反轉原則) 是設計原則(我們應該怎麼做),而 IoC (控制反轉) 是一種更廣泛的架構思想。透過 IoC,我們將物件的控制權轉移給框架或介面;DIP 則是 IoC 的一種具體實現方式,專注於透過「抽象」來解除耦合。
Q2:為什麼過度依賴具體實作 (Concrete Implementation) 會很危險?
A:因為這會導致「僵化性」。當高層模組直接建立低層模組的實例時,任何低層的細微變動(如:更換資料庫連線字串的格式)都可能迫使高層模組跟著修改、重新測試並部署。這就像衣服疊得太亂,想拿最底下那一件就得翻亂整疊衣服。
Q3:如何判斷系統中是否需要引入介面 (Interface)?
A:一個好的判斷基準是:「這部分的邏輯未來是否可能更換?」如果您預見資料來源可能從 API 換成資料庫,或是寄信服務可能從 AWS SES 換成 SendGrid,那麼定義一個 EmailService 介面進行依賴反轉,就是極佳的長遠投資。
掌握了 依賴反轉原則,您就掌握了讓複雜系統變簡單的魔杖。持續在生活中練習「役物而不役於物」,讓程式碼與生活同步回歸簡約與高效!
喜歡這篇文章,請幫忙拍拍手喔 🤣