反对 if 行动

阅读数:4899 2009 年 7 月 6 日

话题:敏捷.NET语言 & 开发文化 & 方法

意大利 XP 倡导者 Francesco Cirillo 为他著名的“反对 if 行动”创建了一个网站,一时吸引了不少支持者Francesco 认为

if 所带来的问题主要在于建立了模块(方法、对象、组件等)之间的依赖,也增加了代码路径的分支(这会降低代码的可读性)。

Francesco 举了一个示例,认为如下的代码:

// Bond class
double calculateValue() {
       if(_type == BTP)  { 
        return calculateBTPValue();
       } else if(_type == BOT) { 
                 return calculateBOTValue();    
              } else {
                 return calculateEUBValue();
              }
}

应该使用多态将显式的 if 或 switch 语句消除:

// Bond class
double calculateValue() {
    _bondProfile.calculate();
}
// AbstractBondProfile class
abstract double calculate();

// classe BTPBondProfile >> AbstractBondProfile
double calculate() {
    ...
}
// classe BOTBondProfile >> AbstractBondProfile
double calculate() {
    ...
}
// classe EUBondProfile >> AbstractBondProfile
double calculate() {
    ...
}

Francesco 认为这样做的好处在于

当我们需要增加新的 bond 类型时,只需创建一个新的类型来保存这部分独立逻辑即可。

创建抽象类或接口并非是改进的唯一方式,我们的目的是将程序变得更灵活、更易于交流、更容易测试、并且随时拥抱变化。

对于“反对 if 行动”,Matteo Vaccari补充了几点做法

  修改前 修改后
直接使用布尔运算结果
if (foo) {
  if (bar) {
    return true;
  }
}
if (baz) {
  return true;
} else {
  return false;
}
return (foo && bar || baz); 
使用辅助函数
if (x > y)
  return x;
return y;
return max(x, y);
灵活使用 0 的作用
int arraySum(int[] array) {
  if (array.length == 0) {
    return 0;
  }
  int sum = array[0];
  for (int i=1; i < array.length; i++) {
    sum += array[i];
  }
  return sum;
}
int arraySum(int[] array) {
  int sum = 0;
  for (int i=0; i < array.length; i++) {
    sum += array[i];
  }
  return sum;
}

不过社区中对此也有不同看法,有人讽刺道:

哦,这里我发现了一个问题。尊敬的客户,您前一个顾问使用了“if”语句,让我把这个函数替换为 80 个新类,这样显得更加敏捷一些。

Don't Repeat Yourself 看上去更敏捷一些。更好的做法是:等到出现第 3 遍重复代码的时候才去重构(不过也别等太久了)。这样可以避免很多无所谓的代码,也可以让程序员有更灵活的选择余地。

也有人为“反对 if 行动”进行了补充:

我想这个行动并不是说“删除所有的 if 代码”,而是将类型判断逻辑使用多态进行替换。如果把它称为“反对类型字段行动”会更合适一些。

“反对 if 行动”也在征集“if-free”的代码示例,您也可以在那里提交代码片断。事实上,国内社区也提出了不少消除繁琐 if 语句的案例,如:

您在这方面是否也有独特的经验呢?不妨也一起分享出来吧。