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

    特别说明:希望网友们在转载本文时,应当注明作者或出处,以示对作者的尊重,谢谢!

  • 相关阅读:
    [代码]codeforces 150c Smart Cheater
    [存档]Young Tableau
    [转载][毁童年]种太阳
    [转载]教练,我也想再要一个同桌
    静夜
    CodeProject每日精选: Applications
    设计模式 装饰模式(Decorator) 精选经验合集
    槟榔味儿办公室
    盗不盗非常盗
    月末之周末
  • 原文地址:https://www.cnblogs.com/mooodo/p/howDoesCodeReuseDo.html
Copyright © 2020-2023  润新知