• 谨慎地创建和使用工具类 - [Software Development]

    2008-11-02

    Utility, Helper, Common常被用来给静态的工具类命名,我一看到这样的东西就不由地皱眉头,不是说工具类没有用,而是因为看到太多工具类的误用了。下面是我在工作中遇到的两个例子。

    有一次,看到下面的Java代码:

    public void doSomething(){
    if(StringUtil.isEmpty(this.value)){
    // do this way ...
    }
    else{
    // do that way ...
    }
    }

    代码使用了StringUtil,觉得奇怪,难道内建的String功能不够强大,还要搞出个StringUtil?直觉告诉我,isEmpty是多余的,一定是哪里做错了什么。isEmpty是这么实现的:

    public class StringUtil{
    // ...
    public static boolean isEmpty(String s){
    return s == null || s.trim() == "";
    }
    // ...
    }

    这个实现比较奇怪,它做了null check,还做了trim。表面上看,是要做这些处理才算完整,因为上面那个业务对象的值是从Web页面上映射过来的,可能含有多余的空格,所以要trim;这个值要存入数据库,对应的字段是可以为null的,所以要做null check;由于有很多类似的情况,所以写了这个函数以备重用。

    但实际上,isEmpty这个怪异的实现只是把问题隐藏起来而已。第一,Web页面上用户输入的值应该在设置给后台业务对象前被trim掉,而不应该继续污染业务对象;第二,通过空字符串或null来表达同一个业务概念让问题复杂化了,在两者中取一个就行了,比如统一使用空字符串,数据库字段不允许为null。实现这两点,逻辑清楚了,这个工具函数也没有存在的必要了。

    另外一个例子,有这样一个Javascript工具类函数:

    function showErrorMsg(errorElementId, errorMsg, isOnBlur, isWarningMsg){
    var element = document.getElementById(errorElementId);
    // codes retrieve/create errorMsgDiv for element.
    // codes add errorMsgDiv to dom tree.

    if (!isWarningMsg) {
    $(element).addClass('errorBoxClass');
    }

    if (isOnBlur) {
    errorMsgDiv.setAttribute('errorType', 'blur');
    }
    else{
    errorMsgDiv.setAttribute('errorType', 'submit');
    }

    if(isWarningMsg){
    $(errorMsgDiv).addClass('alertMsgClass');
    }
    else{
    $(errorMsgDiv).addClass('errorMsgClass');
    }
    }

    这个函数是在数据验证失败的时候在一个Dom元素后面动态地增加一个Div显示错误信息并设置合适的class。随着需求的增加,这个函数被不断地加进新的职责。isOnBlur表示函数是否在Blur事件中被调用,其实是表示验证的等级,在blur时做的验证还是在submit时做的验证,以备在后面的clearErrorMsg函数中做以判断,决定是否能将信息清楚(需求要求在blur事件验证成功后也不能清除由于submit时产生的错误消息)。isWarningMsg表示是否是警告信息,会使用不同的class。

    这里有明显的smell,调用者通过isOnBlur来表示验证的等级,并将这一信息设置到div中,在clearErrorMsg函数中再拿出这个信息做判断来实现要求的逻辑,这些已经不像是一个工具函数该做的事情了。不仅如此,调用者还要选择在什么时机调用这些函数,以及调用的顺序,稍有闪失验证逻辑就有问题。这些smell都在说,通过向工具函数中增加tricky的逻辑来应对日益丰富的验证功能已经不可行了。应将验证逻辑封装在其它对象中,工具函数还可以依旧如以前那样纯洁。

    静态工具函数是可以增加重用性,但这种重用往往停留在,也只应该停留在,技术细节上,而主要不是应用或业务逻辑上。过分依赖静态工具函数容易让我们错失将应用和业务逻辑封装到对象中的机会,而很可能去给工具函数增加了本不属于它的tricky逻辑,也只能实现较低级别的重用。

    静态工具函数有时能够减弱不良设计造成的影响,这不是我们想要的,我们希望放大不良设计的影响,从而推动进一步重构。

    为了防止静态工具类变成垃圾场,再给工具类加函数之前先想一想是否真的需要这样做,通过改进设计,封装应用或业务逻辑是否就可以避免这个函数了。


    收藏到:Del.icio.us