代码复用应该这样做(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