• 代码复用应该这样做(2)


    以上是对一个对象中各函数间的代码复用。另一种情况是这被比较的两份或者多份代码不在同一个对象中,这应该怎么办呢?我们可以采用的办法比较多,首先一种比较直观的办法就是运用“抽取类”将共同的部分抽取到一个工具类中,为其它各类所调用。比如,看看这个例子:

    我们有个遗留系统在大量地方需要获取当前服务器时间,该功能在过去版本中这样写:

    1 Date now = new Date();

    后来JDK升级以后该方法被废掉了,所有获取当前时间的代码都要被改成这样:

    1 Calendar calendar = Calendar.getInstance();
    2 Date now = calendar.getDate();

    但随后的问题就来了,整个系统有数十处获取当前时间的代码,它们都要被修改,这就很烦人了。此外,还有一些代码是要获取当前的月份:

    1 Calendar calendar = Calendar.getInstance();
    2 int thisMonth = calendar.get(Calendar.MONTH)+1;

    它们都十分相似,但功能又不完全相同。这时,可以将它们提取出来形成一个DateUtil工具类:

     1 /**
     2  * 处理日期时间工具类
     3  * @author fangang
     4  */
     5 public class DateUtil {
     6     /**
     7      * @return 获得当前Calendar
     8      */
     9     public static Calendar getCalendar(){
    10         Calendar calendar = Calendar.getInstance();
    11         return calendar;
    12     }
    13     /**
    14      * @return 获得今年
    15      */
    16     public static int getThisYear(){
    17         return getCalendar().get(Calendar.YEAR);
    18     }
    19     /**
    20      * @return 获得本月
    21      */
    22     public static int getThisMonth(){
    23         return getCalendar().get(Calendar. MONTH)+1;
    24     }
    25     /**
    26      * @return 获得当前时间
    27      */
    28     public static Date getNow(){
    29         return getCalendar().getDate();
    30     }
    31 }

    这样,系统中所有获得当前时间与当前月份的代码都这样写:

    1 Date now = DateUtil.getNow();
    2 int thisMonth = DateUtil.getThisMonth();

    采用工具类的方法非常实用,被合并的代码即使分散在系统的各个角落,各自功能没有任何联系,都可以采用。该方法可以有效解决代码变更中的“散弹枪式修改(Shotgun Surgery)”问题,即一个需求的变更导致分散在各处的代码修改。如果将分散在各处的代码统一成一个工具类,则需求的变更就仅仅变成对这个工具类的修改,“散弹枪式修改”的问题就得以解决。

    另外一种与该方法比较类似的方法,就是提取并封装成实体类。当重复代码被分散在原程序的多个代码段中,并且其内部存在着较强的业务相关性时,可将这些代码提取并封装成一个实体类,该实体类应当体现出这种业务相关性。在前一种方法中,工具类仅仅表现为一堆方法的集合,而该方法中,该实体类体现的是一定的业务逻辑,并在使用时需要实例化。看看这个例子:

    在许多系统中,诸如Servlet或Action都需要通过request读取前端传送的数据,但这些数据需要频繁通过转换转成各种数据类型。这些转换过程往往会造成大量重复代码:

    1 double amount = Double.valueOf(req.getParameter(“amount”)).doubleValue();
    2 int count = Integer.valueOf(req.getParameter(“count”)).intValue();
    3 String name = new String(req.getParameter(“name”).getBytes(),"GBK");
    4 Date now = DateUtil.getDate(req.getParameter(“now”),"yyyy-MM-dd");

    但所有这些操作都存在一个内在的联系,即都是在通过request读取前端数据,因此我们将它们封装成一个Reader类,并将request作为参数,在实例化时传递过去:

     1 /**
     2  * 默认的读取器,读取POST提交的数据
     3  * @author fangang
     4  */
     5 public class Reader {
     6     private HttpServletRequest req = null;
     7     /* 
     8      * Constructor with request
     9      * @param HttpServletRequest
    10      */
    11     public Reader(HttpServletRequest req) throws IOException {
    12         this.req = req;
    13     }
    14     /* 
    15      * @param name of a parameter
    16      * @return the data of Object
    17      */
    18     public Object getData(String name) throws IOException {
    19         Object data = req.getParameter(name);
    20         if (data==null) {
    21             throw new IOException("读取器未读出相应数据:"+name);
    22         }
    23         return data;
    24     }
    25     /* 
    26      * @param name of a parameter
    27      * @return the data of double
    28      */
    29     public double getDouble(String name) throws IOException {
    30         return Double.valueOf(getString(name)).doubleValue();
    31     }
    32     /* 
    33      * @param name of a parameter
    34      * @return the data of int
    35      */
    36     public int getInteger(String name) throws IOException {
    37         return Integer.valueOf(getString(name)).intValue();
    38     }
    39     /* 
    40      * @param name of a parameter
    41      * @return the data of String
    42      */
    43     public String getString(String name) throws IOException {
    44         return getData(name).toString();
    45     }
    46     /* 
    47      * @param name of a parameter
    48      * @return the data of Chinese String
    49      */
    50     public String getChinese(String name) throws IOException {
    51         return new String(getString(name).getBytes(),"GBK");
    52     }
    53     ……
    54 }

    这样,在编写这类似的程序时就不再那么麻烦了:

    1 Reader reader = new Reader(req);
    2 double amount = reader.getDouble(“amount”);
    3 int count = reader.getInteger(“count”);
    4 String name = reader.getChinese(“name”);
    5 Date now = reader.getDate(“now”);

    这样的设计好处多多。首先,如果出现什么因JDK的调整需要修改转换程序的写法时,修改Reader类中的相应方法就可以了,过去这类似的“散弹枪式修改”得到抑制。其次,如果有其它的方法过去需要传递request的,现在将不再需要传递,而是替换成传递reader,这将可以一定程度实现对web容器的解耦。不仅如此,如果今后需要修改设计,不使用request传递数据,而是采用其它方式,则只需要修改Reader的实现就可以了,程序可维护性与易变更性得到提高。(续)

    相关文档:

    遗留系统:IT攻城狮永远的痛
    需求变更是罪恶之源吗?
    系统重构是个什么玩意儿
    我们应当改变我们的设计习惯
    小步快跑是这样玩的(上)
    小步快跑是这样玩的(下)
    代码复用应该这样做(1)
    代码复用应该这样做(2)
    代码复用应该这样做(3)
    做好代码复用不简单(1)

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

  • 相关阅读:
    js 所有事件列表
    ironpython
    BAT批处理基本命令总结
    cmd命令行大全 dos命令 cmd命令整理
    Oracle向MySQL迁移
    python html转pdf
    python3 图片验证码
    Python 发送邮件
    如何卸载虚拟机
    django开发网站 让局域网中的电脑访问你的主机
  • 原文地址:https://www.cnblogs.com/mooodo/p/howDoesCodeReuseDo2.html
Copyright © 2020-2023  润新知