前面我們討論了小步快跑,是不是開始被雷到了,做了這么多年程序猿,原來程序可以這么開發。是的,小步快跑是一個十分新穎的概念,也許你一時半會兒還不能完全領悟,或者不能欣然接受,因為它太前衛了,與我們傳統的思維大相徑庭。但是,就像一部精彩的小說,我會慢慢揭開它神秘的面紗,你會慢慢領悟,進而接受。總之,小步快跑就是“活在當下”,做現在的設計,不必過多考慮將來(即使考慮,也是完全可以預見的將來),因為我們有重構。我倡導的是一種快樂的生活方式,編程是一種享受,而不是一個苦差。
然而,我們的問題並沒有完全解決,因為我們面對的是遺留系統。遺留系統,就是你對它做任何事情,都要先閱讀一大堆晦澀難懂的代碼,然后艱難得對其進行修改。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)
特別說明:希望網友們在轉載本文時,應當注明作者或出處,以示對作者的尊重,謝謝!