Teaser Image

mindwind

十日画一水,五日画一石




通常有经验的程序员写出来的代码一开始可读性都是不错的,但随着需求变更,维护人员变化,慢慢架构开始腐化,代码开始变的混乱起来。 还有就是有时仅仅为了完成功能,而完全忽略了代码的可读性(非功能性需求)。 编程时如何保持对代码可读性的持续关注呢,举个小小的例子吧。 有一个简单的需求,写一个方法生成一个字符串key值,传入全类名、调用方法名返回key值,key的长度受外部条件约束不能超过50个字符。 首先看下面这个实现:

private String generateKey(String service, String method) {  
    String head = "DBO$";  
    String key = "";  

    int len = head.length() + service.length() + method.length();  
    if (len <= 50) {  
        key = head + service + "." + method;  
    } else {  
        service = service.substring(service.lastIndexOf(".") + 1);  
        len = head.length() + service.length() + method.length();  
        key = head + service + "." + method;  
        if (len > 50) {  
            key = head + method;  
            if (key.length() > 50) {  
                key = key.substring(0, 48) + ".*";  
            }  
        }  
    }  

    return key;  
}

方法实现不复杂,很短,看起来也不错。 分析下逻辑:

  1. 首先 key由固定的头(head)+ service(全类名)+ method(方法)组成,若小于50字符,直接返回。
  2. 若超过50字符限制,则去掉包名,保留类名,再判断一次,若此时小于50字符则返回。
  3. 若还是超过50字符限制(可能有个变态的家伙起了个很长的类名或方法名),则连类名一起去掉,保留头和方法再判断一次若小于50字符则返回。
  4. 最后如果有个变态长的方法(46+个字符),没办法,只好暴力截断到50字符返回。

这个实现最大限度的在生成key中保留全部的有用信息,对超过限制的情况依次按信息重要程度的不同进行丢弃。 这里只有一个问题,这个业务规则只有 4 个判断,实现进行了三次 if 语句嵌套,还好这个方法比较短,可读性还不成问题。 而现实中很多业务规则比这复杂的多,以前看过一些实现的 if 嵌套多达10层的,方法也长的要命。 当然一开始没有嵌套那么多层,只是后来随着时间的演变,业务规则发生了变化,增加了。 后来的程序员就按照这种方式继续嵌套下去,慢慢演变至此,到我看到的时候就有10层了。 程序员有一种编程的惯性,特别是进行维护性编程时,一开始接手一个别人做的系统,不可能一下能了解和掌控全局。 当要增加新功能时,在原有代码上添加逻辑,很容易保持原来程序的写法惯性(考虑这样写更安全)。 所以一个10层嵌套 if 的业务逻辑方法实现,第一个程序员也许只写了3次嵌套,感觉还不错不失简洁。 后来写4、5、6层的程序员就是懒惰不愿再改,到了写第 8、9、10层的程序员时基本很可能就是不敢再乱动了。代码最后就变成了一大坨*。 考虑下上面个简单的例子,怎么改改比较好呢。我自己写了个实现如下:

private String generateKey(String service, String method) {  
    String head = "DBO$";  
    String key = head + service + "." + method;  

    // head + service(with package) + method  
    if (key.length() <= 50) {  
        return key;  
    }  

    LOG.info("key = " + key);  

    // head + service(without package) + method  
    service = service.substring(service.lastIndexOf(".") + 1);  
    key = head + service + "." + method;  
    if (key.length() <= 50) {  
        return key;  
    }  

    // head + method  
    key = head + method;  
    if(key.length() <= 50) {  
        return key;  
    }  

    // last, we cut the string to 50 characters limit.  
    key = key.substring(0, 48) + ".*";  
    return key;  
}

最后,我在写这个小方法时想起好多年前读到过的一个编程建议:函数最好只有一个出口,一个 return。 不幸的是这个实现有 4 个 return,而前一个实现确实只有一个 return,所以我想说一切编程原则、建议、设计模式都不是绝对的。 它们犹如兵法,可参考,可学习,但兵法的运用还要看天时、地利、人和。 编程也一样,一切根据你需要解决的实际问题去灵活运用,自然能写出最适合的程序。