Guice隨筆

隨着時間的推移. 當初吵翻了天的依賴注入再也不是什麼新鮮玩意兒.

在日復一日給資本家拉磨的平淡中, Spring和自己寫的小玩具也都被扔在記憶的垃圾堆裏不必提起. 對Guice的激情也逐漸磨滅到麻木和冷淡了.

日常一些郵件列表中, 仍然能看到對使用Guice之類框架的反感或者死忠的兩種截然不同的觀點碰撞, 甚至於是否使用依賴注入也仍然沒有塵埃落定.

個人從前是依賴注入的堅定粉絲, 也是Guice的歡呼者. 到現在我在需要使用一個框架的時候還是會使用Guice, 但是, 各位看官, 如果你們以前曾經見過那個揮舞着Guice超級大棒見什麼都砸幾下的不屈身影, 他已經移民去火星了.

如今, 我理想的使用Guice的方法. 就是說我有一堆類, 互相有依賴關係, 假設A依賴B, C, D; B依賴X, Y, Z; C依賴於D, B和D需要是Singleton, 那麼就寫成:
[code]
class A {
@Inject A(B b, C c, D d) {...}
}

@Singleton
class B {
@Inject B(X x, Y y, Z z) {...}
}

class C {
@Inject C(D d) {...}
}

@Singleton
class D {
@Inject D(...) {}
}
[/code]

如果你對Guice熟悉的話, 就會看出來這正是Guice幼兒園小班級別的代碼啊.

這就對了, 幾年用下來的經驗告訴我, Guice對這種最簡單的情況的處理方式是最簡單, 最直接的. 不需要寫什麼Module, 不需要什麼binding, 那些東西帶來太多的複雜性.

很多對依賴注入或者Guice的疑慮主要集中在代碼不容易理解, 依賴關係被隱藏在Module中不容易發現等等. 但是我發現這些疑慮在儘量有意減少module和binding的前提下, 都不再是問題.

比如我在讀A類的代碼, 需要知道B到底是怎麼回事, 直接一個F3(Eclipse), 就到了B的代碼裏面. 然後需要知道X是怎麼回事, 再一個F3. 許多在線代碼檢查工具也支持這種簡單的導航.

相比之下, 手工注入(就是說, 某個地方你需要手工調用new A(b, c, d))反而難於理解, 因爲我還要去找new A()在什麼地方調用的, B, C, D都是怎麼傳遞進來的.

當然, 這個前提是我的B, C, D, X, Y, Z都是類而不是interface. 我發現在一個大的代碼庫裏, interface很多時候是有害的. 因爲它們人爲的增加了一個間接層, 增大了跟蹤導航閱讀代碼的難度. 在加上在Guice裏你就需要額外的Module, 額外的binding, 額外的@ForApple, @ForOrange的註解, 然後Module多了, 你就要面對如何管理整個項目, 怎麼樣在不同的main()函數裏選擇不同的Module, 而又避免不能把一個Module通過不同的父Module安裝兩遍等等複雜討厭的問題.

interface只有在真正存在那麼一個自然的抽象的情況下才是適合的. 比如, 一個List和ArrayList就是兩個不同的抽象. java.sql.Connection和任何特定的Connection實現也是兩個不同的抽象.

而如果你需要寫一個User, Authenticator, 或者TaskMonitor, 直接寫就是了. 不管三七二十一上來先一個IUser接口加一個User實現, 或者一個User接口加一個UserImpl實現, 那就是人爲增加的複雜性. 類和接口命名的尷尬雷同就已經表明這根本不是一個自然的抽象.

okay, 理想如此, 事實上當然不可能永遠都沒有一個接口, 兩個實現的情況出現. 不過, 如果Module和binding只在有這種需求的時候才寫的話, Guice代碼也還可以相對容易地管理和理解. (當然, 還要小心robot leg問題的出現, 然後如果你用assistedinject來繞過這個問題的話, 又是一些額外的複雜性需要管理和維護了).

嗯, 嗯. 大致就隨筆到這裏. 回頭我還會寫寫爲什麼我不喜歡assistedinject, 以及爲什麼Guice的Module會難以管理等等.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章