使用 Dependency Injection 框架: dagger2 心得

最近同事推坑 Dagger2,用了以後發現很好很強大,尤其在 class 依賴關係複雜的情況下可以把 Java 程式架構拆得很乾淨,只好怒發一篇我覺得為什麼 Dagger2 很好用的心得。

使用情境

假設現在你有一個商務邏輯元件:Bussiness,依賴多個 Service 元件,其中一個 Service 又依賴一個 Dao 物件,程式碼範例如下:

因此當我們要使用 Bussiness 元件時勢必要先建構 service 元件和 Dao 元件,其中一種寫法是在 Business 建構子中建構其他依賴物件:

這種寫法顯而易見的缺點會是 Business 物件和 Service、Dao 物件耦合高,後續不管是抽換底層物件或單元測試都不好寫。

另一種作法是先將所有依賴的物件的建構好,在建構 Business 物件時將這些物件都傳入 Business 物件,也就是 Dependency Injection:

這種寫法的壞處就是在建構 Business 物件時要先一一把所有依賴的物件都創建好,想像如果你的 Business 物件依賴了十個 Service 物件,每次使用 Business 物件前都要先建構一堆依賴物件。

既然要注入物件,何不讓系統自動幫我們注入,我們只跟系統說我需要用到什麼物件就好了?

用 Dagger2 來解決物件依賴的問題吧!

我們先將原本的 code 稍微修改一下:

利用 Inject annotation 把需要注入的物件和提供注入的物件連結在一起,接下來用 Dagger2 的 component 指定 Business 物件需要被注入物件:

接著讓 Dagger2 自動 gener code 以後,即可用簡短的兩行 code 建構一個 Business 物件使用:

其中第五行的 DaggerBusinessComponent 是根據 BusinessComponent interface 自動產生的 code。

經過第五行的 inject,Business 內所需要的物件都會被自動注入,不用自己初始化。

當然,如果僅僅只是如此 Dagger2 還稱不上強大,接下來我們來介紹 module。

如果同時存在同類型的不同物件需要被注入呢?

延續上面的例子,如果 Dao 是個 interface,而我們有兩個實作 Dao 的物件:DaoMongoImpl1、DaoMysqlImpl

接著我們在用一個 module 來提供可被注入的 Dao 實作:

用 Module annotation告訴 Dagger2 這是一個提供被注入物件的 module,用 Provides annotation 的方法用來提供被注入物件。以這一小段 code 來看我們提供兩種可被注入的 Dao 物件,並用 Named annotation 來識別。

接著修改一下 component interface,指定 module 的 class

第一行的 @Component(modules = {DaoModule.class}) 告訴 Dagger2 使用該 component 當要注入物件時,優先找 Module 裡面提供注入的物件,找不到再去用 Inject 標注的建構子(如第一個例子)。

接著我們修改 Service 物件:

之後我們可以藉由編輯 Named 的值方便的抽換 Dao 的實作方法。

甚至也可以透過用不同的 component 指定不同的 module,達到彈性產生各種不同 Dao 實作方式的的 Service:

透過 component 和 module 的組合,我們可以解耦高階物件和低階物件的依賴關係,在不修改 Business 或 Service 程式碼的情況下抽換底層的 Dao。

完整的 sample code 放在這裡

references:

Android:dagger2让你爱不释手-基础依赖注入框架篇

控制反轉 (IoC) 與 依賴注入 (DI)