代碼復用應該這樣做(1)


前面我們討論了小步快跑,是不是開始被雷到了,做了這么多年程序猿,原來程序可以這么開發。是的,小步快跑是一個十分新穎的概念,也許你一時半會兒還不能完全領悟,或者不能欣然接受,因為它太前衛了,與我們傳統的思維大相徑庭。但是,就像一部精彩的小說,我會慢慢揭開它神秘的面紗,你會慢慢領悟,進而接受。總之,小步快跑就是“活在當下”,做現在的設計,不必過多考慮將來(即使考慮,也是完全可以預見的將來),因為我們有重構。我倡導的是一種快樂的生活方式,編程是一種享受,而不是一個苦差。

然而,我們的問題並沒有完全解決,因為我們面對的是遺留系統。遺留系統,就是你對它做任何事情,都要先閱讀一大堆晦澀難懂的代碼,然后艱難得對其進行修改。IT攻城獅最大的痛不是如何面對客戶異想天開的新功能,而是面對遺留系統時情何以堪。代碼質量是幾乎所有遺留系統共同的問題,你可以罵領導如何只重效益不重質量,你可以罵需求分析如何打醬油而不控制用戶需求,你的前任代碼寫得如何爛。這些多說無益、人人都懂。從自己做起,寫好自己的每一段代碼,就是對整個項目最大的貢獻。

怎樣才能寫好每一段代碼呢?這里我不想講太多的理論,這些理論到處都有,真不需你在這里費口舌。大家回想一下,我們在以往編寫代碼的過程中,有多少次在復制粘貼代碼?不用說,一定是無數次,據說程序員操作最熟練的快捷鍵就是“ctrl+C”與“ctrl+V”。毫無疑問,我們編寫的代碼中有許許多多的功能是相似甚至完全相同的,關鍵在於你怎樣去看待它。復制粘貼是最簡單常用的方式,但在一個軟件系統中,如果同一功能被反復地復制粘貼了,一旦這段代碼需要變更時,那簡直就是一種災難。代碼重復的問題普遍存在於許多遺留系統中,但我們很少會重視它,特別是在中國。然而,它就是糟糕代碼的元凶,系統維護越來越困難的源頭。

因此,今天我們來談一談代碼復用。毫無疑問,我們首先應當建立起代碼復用的意識。但光有意識是遠遠不夠的,真正的考驗是一個機會擺在你面前,而你要懂得該怎樣做,這才是考驗程序員是否優秀的地方。它需要有相當的面向對象分析與設計的理論知識(說實話,這部分是國內軟件工程師最應當掌握卻普遍缺失的知識,所謂的大牛就是能夠用開源框架架構系統的人),與相當的分析設計經驗。

不論怎樣,我們先看看要解決代碼復用的問題應該怎樣做,然后再看看添加新代碼時應該怎樣復用。OK,翠花,上示例!

總體來說,第一步應該是比較代碼。將功能相同或相似的代碼進行比較(可以使用一些代碼比較工具),然后將這些代碼段中彼此相同的部分標注出來,運用“抽取方法”將其抽取到另一個函數中。這個函數中的代碼,就是我們需要復用的代碼。

假如這被比較的兩份或者多份代碼存在於同一個對象中,則將這段被抽取出來的函數作為它們共同的函數,為其它各份代碼所調用。比如,有這樣一段代碼:

 1 /**
 2  * 通過DWR獲得當前的request、response、session等信息  3  * @author fangang  4  */
 5 public class DwrContext {  6     /**
 7  * @return 當前的Request  8      */
 9     public static HttpServletRequest getRequest(){ 10         return WebContextFactory.get().getHttpServletRequest(); 11  } 12     /**
13  * @return 當前的Response 14      */
15     public static HttpServletResponse getResponse(){ 16         return WebContextFactory.get().getHttpServletResponse(); 17  } 18     /**
19  * @return 當前的Session 20      */
21     public static HttpSession getSession(){ 22         return WebContextFactory.get().getSession(); 23  } 24 }

大家注意到在這段代碼的多個函數中都有這段代碼WebContextFactory.get(),也就是說這段代碼是幾個函數相同的部分。我們運用“抽取方法”將其抽取出來,放進getContext()函數中,而讓其它的函數不再使用重復的代碼,而是替換成對這個函數的調用:

 1 /**
 2  * 通過DWR獲得當前的request、response、session等信息  3  * @author fangang  4  */
 5 public class DwrContext {  6     /**
 7  * @return DWR的WebContext  8      */
 9     private static WebContext getContext(){ 10         return WebContextFactory.get(); 11  } 12     /**
13  * @return 當前的Request 14      */
15     public static HttpServletRequest getRequest(){ 16         return getContext().getHttpServletRequest(); 17  } 18     /**
19  * @return 當前的Response 20      */
21     public static HttpServletResponse getResponse(){ 22         return getContext().getHttpServletResponse(); 23  } 24     /**
25  * @return 當前的Session 26      */
27     public static HttpSession getSession(){ 28         return getContext().getSession(); 29  } 30 }

這里還有一種情況也相當普遍,就是一個對象中的函數重載,比如這段代碼:

 1 /**
 2  * 初始化工廠。根據路徑讀取XML文件,將XML文件中的數據裝載到工廠中  3  * @param path XML的路徑  4      */
 5     public void initFactory(String path){  6         // 實現讀取path路徑下的XML文件,然后裝置工廠
 7  }  8     
 9     /**
10  * 初始化工廠。根據路徑列表依次讀取XML文件,將其中的數據裝載到工廠中 11  * @param paths 路徑列表 12      */
13     public void initFactory(String[] paths){ 14         this.paths = paths; 15         for(int i=0; i<paths.length; i++){ 16             // 實現讀取paths[i]路徑下的XML文件,然后裝置工廠
17  } 18     }

這里加粗的部分被我省略了,其實內容很多,並且代碼重復。這時我們為什么不設計成讓后一個函數讀取前一個函數呢?具體說就是這樣:

 1     /**
 2  * 初始化工廠。根據路徑讀取XML文件,將XML文件中的數據裝載到工廠中  3  * @param path XML的路徑  4      */
 5     public void initFactory(String path){  6         // 實現對一個路徑裝置工廠
 7  }  8     
 9     /**
10  * 初始化工廠。根據路徑列表依次讀取XML文件,將XML文件中的數據裝載到工廠中 11  * @param paths 路徑列表 12      */
13     public void initFactory(String[] paths){ 14         this.paths = paths; 15         for(int i=0; i<paths.length; i++){ 16  initFactory(paths[i]); 17  } 18     }

(續)

 

相關文檔:

遺留系統:IT攻城獅永遠的痛
需求變更是罪惡之源嗎?
系統重構是個什么玩意兒
我們應當改變我們的設計習慣
小步快跑是這樣玩的(上)
小步快跑是這樣玩的(下)
代碼復用應該這樣做(1)
代碼復用應該這樣做(2)
代碼復用應該這樣做(3)
做好代碼復用不簡單(1)

特別說明:希望網友們在轉載本文時,應當注明作者或出處,以示對作者的尊重,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM