Sky's blog

          我和我追逐的夢

          常用鏈接

          統計

          其他鏈接

          友情鏈接

          最新評論

          淺談spring和依賴注入的價值

          javaeye上看到有帖子,置疑spring和依賴注入的價值,回復內容整理如下:

          依賴注入對設計有利,而spring則促進了依賴注入的使用。

          如果業務處理類,它所使用的倚賴,都是依靠在這個類內部實現或者查找,那么必然使得正常的業務邏輯和獲取依賴的方法混在一起。

          我取個最簡單的場景,某個注冊的工作類,它需要獲取當前"容許的用戶名的最大長度",這個依賴非常簡單吧?基本每個注冊類都有這個限制,我們現在 把場景考慮的全面一點,對于復雜一點的系統,這個最大長度的限制可能來源很多,比如配制文件,數據庫,可能類工作在前臺比如web而配制在后臺,可能需要 和第三放系統一起工作而需要到第三方系統中獲取而對方只提供web service...

          這么一個簡單的依賴,“用戶名的最大長度”,如果用依賴注入,只要一個簡單的setUsernameMaxLength()方法就可以搞定。考慮 上面那么多種可能都出現,最惡劣的情況是要求一個系統可以同時支持然后通過配制方式進行,這對于將一個產品賣給n家客戶的公司來說是最正常不過的要求。

          那么我們來看一個很有"spring"風格的采用依賴注入的設計,通常都將會是這樣:

          注冊工作類:
          public void RegisterWork {
          public void setUsernameMaxLengthProvider(UsernameMaxLengthProvider provider) {
          int maxLength = provider.getUsernameMaxLength(); //簡單獲取結果,不管provider細節
          }
          ....
          }
          “用戶名的最大長度”的提供者接口
          public interfacd UsernameMaxLengthProvider {
          public int getUsernameMaxLength();
          }
          “用戶名的最大長度”的提供者則可以有以下實現,都只負責讀取數據,不關心數據被誰使用,怎么使用:
          1.直接提供,可以用spring 構造函數方式注入usernameMaxLength值,也可以junit測試時直接new DirectdProvide對象
          public class DirectdProvider implements UsernameMaxLengthProvider {
          private int usernameMaxLength = 10;
          public int getUsernameMaxLength() {
          return UsernameMaxLength;
          }
          public DirectdProvider (int usernameMaxLength) {
          this.usernameMaxLength = usernameMaxLength;
          }
          }
          2.讀本地配制文件
          public class LocalConfigFileProvider implements UsernameMaxLengthProvider {
          public void read(File configFile) {
          usernameMaxLength = ....
          }
          }
          3.類似的從數據庫讀取 DatabaseProvide
          4.類似的web service從第三方讀取 WebServiceProvider
          5.其他的可能擴展的方式

          開發時邏輯清晰,代碼可讀性超強,基本看類名和函數名搞定。測試時,輕松測試RegisterWork,testcase中只要 worker.setUsernameMaxLengthProvider(new DirectdProvider(20))就搞定。而針對UsernameMaxLengthProvider的幾個實現類,更是輕松使用junit,每 個都覆蓋一遍。

          呵呵,現在我們可以砍刀,最簡單的一個int型的“用戶名的最大長度”,都可能遭遇如此復雜的場景。如果不用倚賴注入,而是選擇在RegisterWork中自己搞定“用戶名的最大長度”的獲取,那么可能要遇到以下問題:
          1. RegisterWork極其復雜,可以想像類似的依賴肯定還有其他
          2. 獲取“用戶名的最大長度”的方式和注冊的業務處理邏輯完全沒有直接聯系,對注冊過程來說它只關注結果,“用戶名的最大長度”是10還是20,而不是到底讀本地文件還是讀數據庫。喧賓奪主了,次要邏輯干擾了主要邏輯
          3. 難于測試。獲取“用戶名的最大長度”的方式的這些代碼,被藏在RegisterWork類中,而且很有可能是private方法不對外暴露(如果不依賴注入還需要暴露嗎?暴露給誰呢),怎么用mock測試?怎么能保證以上幾種的實現都覆蓋到?
          4. 難于擴展。就算上面都搞定了,某一天突然來了一個變態需求,要求用ldap從另一個地方取配置呢?難道再把ldap請求的那一大片代碼也寫到RegisterWork里面?
          5. 更難于被第三方擴展。運氣好,上面這個ldap取配制的變態需求客戶開始沒有要求。順利開發完成測試通過然后準備上線,最后一晚了客戶才發現,"哦,給忘 了,你們想辦法給加上,快,快,明天一早就要上線運行了...你們寫死在代碼里面了?那只能你們修改了原代碼了"。吐血了吧,先罵一頓,可是活還的干啊, 咬牙切齒的把新的實現代碼加上了,還得編譯打包更新重啟...如果是spring多好,單獨寫一個LdapProvider類,測試(這個測試比雜在 RegisterWork里面測試簡單的多)通過后單獨提供這個class仍classpath下,修改spring的配制將原來的 ***Provider替換掉,輕松搞定,甚至可以把這活仍給客戶的開發人員,告訴他們怎么替換就可以了,管你ladp還是其他,誰讓你們需求不明確,自 己擴展去。
          6. 容易出錯。剛吐血完成上面的變態需求,更新完畢,一會客戶電話來了,“...怎么...不正常了?”。又吐血幾升地檢查,終于找出來了,原來是剛才寫 ladp訪問的代碼時不小心改錯了RegisterWork的一個地方,誰讓RegisterWork類有幾十上百個方法好幾千行呢,一時急,又沒有測試 到......可是客戶不會理解的。

          上述的場景,spring + 依賴注入的設計方式,優點很明顯吧。

          再考慮一下維護和二次開發的問題,上面的spring + 依賴注入的代碼,好看易懂,方便擴展,維護起來輕松。如果是那么堆在RegisterWork里面,在那個大堆中代碼要找出這些代碼并讀懂,估計不是件輕松的事情。

          代碼維護是需要成本的,寫出易于維護的代碼,是一個優秀程序員的基本素養,至少,不能讓下一個接手的人罵娘吧?

          posted on 2008-01-11 22:30 sky ao 閱讀(3094) 評論(6)  編輯  收藏 所屬分類: spring & IOC

          評論

          # re: 淺談spring和依賴注入的價值 2008-01-12 11:47 lingos

          樓主的例子為什么非要是spring + 依賴注入的設計方式才能做到?
          只要設計做的好,80%的東西根本就不需要依賴注入,比如
          public interface UsernameMaxLengthProvider {
          public int getUsernameMaxLength();
          }

          public void setUsernameMaxLengthProvider() {
          //獲取接口的實現類
          UsernameMaxLengthProvider provider=HandlerFactory.getInstance().getHandler(UsernameMaxLengthProvider.class)
          }

          配置:
          <handler-config>
          <handlers>
          <UsernameMaxLengthProvider>
          LocalConfigFileProvider
          </UsernameMaxLengthProvider >
          </handlers>
          </handler-config>

          一樣也可以做到hotswap任意接口實現類!
            回復  更多評論   

          # re: 淺談spring和依賴注入的價值 2008-01-12 11:49 lingos

          最是厭惡spring動不動的一堆bean.xml,IOC過渡使用的一個典型!  回復  更多評論   

          # re: 淺談spring和依賴注入的價值 2008-01-14 14:00 楊一

          首先是解耦,解耦后的第二個問題就是代碼的復雜性和可讀性。當然獨立思考的精神總是值得鼓勵的。  回復  更多評論   

          # re: 淺談spring和依賴注入的價值 2008-01-15 09:50 愛上鳥的魚

          挺難!沒有看懂,以后再來拜讀!  回復  更多評論   

          # re: 淺談spring和依賴注入的價值 2008-01-15 23:48 chenge

          呵呵,世界很小,我google到你了。
          那個問題我提的。

          如果只需要一種provider的話,可以如下:
          RegisterWork (){
          provider = new XProvider(); //更換只需要修改這行代碼,也 可以寫成hotswap
          }

          如果需要同時用到不同的provider,可以考慮工廠方法。

          new已經用了十幾年了,就像向前走路,突然變成后退著走很不習慣,也沒感覺到有什么好。

          只有需要聲明式事務處理和AOP才需要spring.  回復  更多評論   

          # re: 淺談spring和依賴注入的價值 2008-02-04 12:17 areon

          現在的人連設計模式都懶得學了 祭出spring來 知其然不知其所以然的 看看GoF,MF的原著吧  回復  更多評論   

          主站蜘蛛池模板: 杭州市| 临海市| 巩留县| 丰台区| 西林县| 民丰县| 福贡县| 疏附县| 曲阜市| 腾冲县| 平罗县| 伊宁市| 静海县| 丹凤县| 大荔县| 长沙市| 永福县| 高密市| 鄂尔多斯市| 台山市| 开封县| 博野县| 甘洛县| 鹿邑县| 青浦区| 泸水县| 中阳县| 南丹县| 阿巴嘎旗| 剑河县| 绥中县| 丹江口市| 武清区| 承德市| 开化县| 丽水市| 兴国县| 安陆市| 合山市| 光山县| 沁阳市|