主动编程
这篇文章的思想来自于Michael Feathers的Offensive Coding。标题直译过来是“攻击性编程”,但是为了避免与攻击性代码混淆。
这篇文章说,与其被动地做参数检查,不如一开始就避免传递错误参数。我们在书写一个函数时习惯于下意识地检查参数的合法性,不合法则返回null呀-1等表示错误的值。这种“防御”式的代码通常会导致不必要的代码复杂度。换个角度思考,如果能通过某种方法确保使用者不会使用错误的参数进行调用,就可以省却函数内部的参数检查。当然不是说参数检查有什么不对,而是要避免不必要的检查。 下面是原文的一个例子。
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;
}
这个函数的问题出在 return null 一行上。如果intParms数组为空,则返回null。但是我们无法保证每个使用者都会检查该函数的返回值。使用者会通过下面的形式来调用该函数:
paramResults = getParameters();
parseParameters(paramResults);
那么后继函数 parseParameters 就必须要进行下面的定义:
public void parseParameters(String[] params) {
if (params != null) {
// do something here
}
}
可见,函数 getParameters()返回不必要的null值导致parseParameters函数不得不对参数进行检查。如果 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为空时,getParameters将返回一个空数组――这个值可以用来表示非法,但是它是一个合法值,使用者可以放心地用这个返回值去调用其后的parseParameters()函数,而parseParameters()函数也可以省去了参数检查。
另一个例子就是private函数的参数检查。private函数只能在package内部调用,而一个成熟的产品是不应当使用非法值去调用函数的,因此不应当进行不必要的参数检查。
我想到了以前进行嵌入式开发时的一些事情。那时客户要求我们开发的库函数不能返回任何错误值――即使内部出现了错误,也应当忽略这个错误而返回正常值。其实这样做是有道理的。假如一个程序由五层组成,每层都要进行错误检查并在出错时将错误报告给上一层,那么,每一层函数除了要将下层的错误原原本本地传给上层之外,还要加上本层可能产生的错误。这样,最低层的10个错误,到了最上层就有可能变成50个错误――这将对最上层(一般是应用层)的编程产生很大压力。
因此,适当情况下避免无谓的错误检查,并对错误情况进行适当的处理而不是简单地传给上层,才能让编程变得简单,变得愉快。