`
dyllove98
  • 浏览: 1380410 次
  • 性别: Icon_minigender_1
  • 来自: 济南
博客专栏
73a48ce3-d397-3b94-9f5d-49eb2ab017ab
Eclipse Rcp/R...
浏览量:38265
4322ac12-0ba9-3ac3-a3cf-b2f587fdfd3f
项目管理checkList...
浏览量:78504
4fb6ad91-52a6-307a-9e4f-816b4a7ce416
哲理故事与管理之道
浏览量:131652
社区版块
存档分类
最新评论

Offensive Coding

 
阅读更多

参考自:http://www.artima.com/weblogs/viewpost.jsp?thread=168511

   一直以来只知道防御式编程,《代码大全》中也专门有一章讲述防御式编程,防御式编程又称被动编程。有一个比喻是说开车时司机只遵守交通规则是远远不够的,还得时刻提防其他意外因素,如马路杀手、其他醉酒的司机等等。对应在编程中就是“不能信任用户的输入,必须执行相应的参数检查”。

   而offensive coding则与此相反,它的中文意思是攻击式编程或者主动编程。

   先看一段简单的代码:

private void addResolution(List<Flag> flags) {
    if (flags != null) {
        if (supportedAdapters.contains(Adapter.LOW) 
                || supportedAdapters.contains(Adapter.MEDIUM)) {
            flags.add(new ResolutionFlag(ADAPTED));
        } 
    }
} 

   这段代码有啥问题?也许有人的第一反应是这段代码应该使用Guard Clause,即改成下面这样:

private void addResolution(List<Flag> flags) {
    if(flags == null)
        return;

    if (supportedAdapters.contains(Adapter.LOW) 
                || supportedAdapters.contains(Adapter.MEDIUM)) {
        flags.add(new ResolutionFlag(ADAPTED));
    }
} 

   但其实问题不在于此,而在于这一行代码:

if(flags == null) {...

   既然是private方法,那么完全可以在类内部自己保证传入的参数flags不为null,而不用进行多余的null check。

滥用null check是糟糕代码的标识。

   null check是不可或缺的。如果你在你的代码中到处传递null,那么接收参数并处理的方法进行null check是必要的。但是这也意味着你的代码很糟糕。你在给自己增加额外负担(所有有参数的方法开头都必须进行null check),你的代码也很难复用。

   另外一个例子:

public String [] getParameters() {
    if (intParms.length  == 0) {
        return null;
    }
    String [] result = new String[intParms.length];
    for(int n = 0; n < intParms.length; n++) {
        result[n] = "parameter " + intParms[n];
    }
    return result;
}

   这段代码有什么问题呢?如果这个方法被你自己在同一个项目中其他地方使用还好,你知道返回值可能为null,所以你会进行null check。而如果是你的同事在使用这段代码,或者这段代码被打包在Jar中被世界上某个角落的另一个程序员使用呢?他们不会像你一样对你的代码这么熟悉。他们很可能忘记null check,比如如下:

// 世界上某个角落的其他程序员Fork在使用你的Jar包中的getParameters方法
public void foo() {
   ...
   int len = new ConcreteService().getParameters().length;
   ...
}

   很可能这段代码在大部分情况下都能正常运行,而不会抛出NullPointerException,于是这位程序员又将这段代码打包到他的Jar包里,之后又有第三个程序员John来使用Fork的Jar包中的foo()方法,如下:

// John使用Fork的Jar包中的代码
public void bar() {
   ...
   new Delegator().foo();
   ...
}

   OK,问题来了,大部分时候这段代码也执行正常,但是某一天bar()突然抛出NullPointerException,你最初挖的“坑”潜伏这么久,终于有人掉进去了。John只能骂一句“好坑!”或者“Oh, shift!”了。那么最初的getParameters()方法该怎么写才好呢?很简单,像下面这样即可:

public String [] getParameters() {
    String [] result = new String[intParms.length];
    for(int n = 0; n < intParms.length; n++) {
        result[n] = "parameter " + intParms[n];
    }
    return result;
}

    现在如果intParms是一个空数组的话,返回的也是一个空数组。但如果intParms是null怎么办呢?很简单,intParms显然是类变量或者类成员,你只需要保证intParms始终不为null即可,比如这样:

private int[] intParms = new int[0];

    然后任何对intParms的修改都不能将其设为null,但可以把它重置为空数组。这样当前类内部的处理负担可能加重了,但其他类的负担却大大减轻,别人也将更乐意使用你的代码。

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics