DI等於介面? 誤會可大了! — 3 (final)
前幾天在靠北工程師看到一則貼文,內容大致如下:
抱怨公司的專案,整體架構上使用過多的介面,每個類別都有一個介面,假設N個類別,就有2*N個元件需要維護,過度複雜。
當爭議的議題出現時,可想像留言區通常會多方激烈論戰,觀點非常多,有人說DI就是這樣、有人說Java就是這樣、有人說Spring就是這樣、也有人說介面不等於DI等等。
看到大家的辯論,讓我意識到大部分開發者對介面與DI的概念有一定程度的誤解,因此想撰寫一篇文章來跟大家闡述介面與DI之間的關係。(當然這只是我開發多年與看書消化的觀點,也不一定正確,如果有任何疑問,歡迎下方留言多多討論)
此文章總共有三篇,每篇皆嘗試釐清一個概念,此篇為最後一篇:
- 了解介面所要解決的問題與使用時機 (1)。
- 了解DI所要解決的問題與使用時機 (2)。
- 了解為什麼DI不等於介面、介面不等於DI (3)。
介面解決之問題
在第一篇文章中,我們提到了介面要解決的問題,不知道大家還記得嗎?
- 封裝shared dependency。
- 封裝volatile dependency。
- 封裝multiple implementations。
- 遵守depedency inversion principle。
當宣告介面不是為了解決上述問題時,需要回頭檢視一下系統的設計是否出了一些問題。
每個介面都是需要維護的元件,要確保介面增加帶來的價值遠遠高於其成本,長遠下來才能降低系統維護成本。
DI & DI Container解決之問題
在二篇文章中,我們提到了DI & DI Container要解決的問題,不知道大家還記得嗎?
DI
- 確保物件生成的方式統一。
- 確保物件從外部獲得dependency,而不是自己產生。
DI Container
- 組裝應用程式 (object composition)
- 控制生命週期
- AOP
當所有應用程式內的物件皆使用DI概念時,物件會放棄物件的生成、生命週期的控制,從最小元件到最大元件皆是如此,最終,所有物件的生成、生命週期的控制、組裝都會下放於程式進入點 (main entry point)。
當上述概念都在程式進入點進行時,我們可以使用DI Container去解決這些問題,而不使用DI Container自己手動在程式進入點組裝物件、生成物件、生命週期控管的方式也有人使用,這稱之為Pure DI,
DI & DI Container & 介面 Overlapping
當讀者都了解DI、DI Container、介面的概念是什麼、解決什麼問題之後,我們就能開始分析介面是否等於DI了。
先說結論: DI不等於介面、介面不等於DI、但兩個概念可以同時運用。
當使用介面時,我們需要產生實作此介面的類別的物件,在DI出現之前,我們會使用singleton + factory來封裝這些物件產生的過程 (第二篇文章),而DI正好也能解決此問題。
DI的概念會讓外部使用者注入實作此介面的類別的物件,而使用介面的客戶端就不需要透過singleton + factory來獲取對應的物件,進而簡化整個程式碼。
所以說,當我們宣告介面解決shared、volatile、multiple implementation、等問題時,通常會搭配DI的概念來提供需要的物件,這就是介面與DI同時應用的部分。
但大部分時候儘管沒有介面我們依然會使用DI來組裝複雜的物件 (第二篇文章),透過DI注入的方式可以降低物件與物件之間的耦合性,讓系統更朝向物件組裝的方式。
這就是為什麼我說: DI不等於介面、介面不等於DI、但兩個概念可以同時運用。
後序
這篇文章是DI/介面系統最後一篇,希望透過我這三篇文章的講解,大家已經了解介面、DI的使用方式,相信如果能夠掌握這兩個概念,對於專案長久的發展下會有莫大的幫助。
結論
- DI不等於介面、介面不等於DI、但兩個概念可以同時運用。
- 釐清各個概念是什麼、解決什麼問題才有辦法分析彼此的差異、相同的部分。