• 简化条件表达式之以卫语句取代嵌套条件表达式(Replace Nested Conditional With Guard Clauses)


    函数中的条件逻辑使人难以看清正常的执行途径。使用卫语句表现所有特殊情况。

    动机:条件表达式通常有2种表现形式。第一:所有分支都属于正常行为。第二:条件表达式提供的答案中只有一种是正常行为,其他都是不常见的情况。

           这2类条件表达式有不同的用途。如果2条分支都是正常行为,就应该使用形如if…..else…..的条件表达式;如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回。这样的单独检查常常被称为“卫语句”。

           Replace Nested Conditional with Guard Clauses (以卫语句取代嵌套条件表达式)的精髓是:给某个分支以特别的重视。它告诉阅读者:这种情况很罕见,如果它真的发生了,请做一些必要的整理工作,然后退出。

           “每个函数只能有一个入口和一个出口”的观念,根深蒂固于某些程序员的脑海里。现今的编程语言都会强制保证每个函数只有一个入口,至于“单一出口”规则,其实不是那么有用。保持代码清晰才是最关键的:如果单一出口能使这个函数更清晰易读,那么就使用单一出口;否则就不必这么做。

    做法:1、对于每个检查,放进一个卫语句。卫语句要不就从函数返回,要不就抛出一个异常。

           2、每次将条件检查替换成卫语句后,编译并测试。如果所有卫语句都导致相同的结果,请使用 Consolidate Conditional Expression (合并条件表达式)。

       范例

       想像一个薪资系统,其中以特殊规则处理死亡员工、驻外员工、退休员工的薪资。这些情况不常有,但的确偶而会出现。

       假设我在这个系统中看到下列代码

    double getPayAmount() {

       double result;

       if (_isDead) result = deadAmount();

       else {

           if (_isSeparated) result = separatedAmount();

           else {

               if (_isRetired) result = retiredAmount();

               else result = normalPayAmount();

           };

       }

    return result;

    };

    在这段代码中,非正常情况的检查掩盖了正常情况的检查,所以应该使用『卫语句」来取代这些检查,以提高程序清晰度。我可以逐一引入卫语句。让我们从最上面的条件检查动作开始:

    double getPayAmount() {
    
       double result;
    
       if (_isDead) return deadAmount();
    
       if (_isSeparated) result = separatedAmount();
    
       else {
    
           if (_isRetired) result = retiredAmount();
    
           else result = normalPayAmount();
    
       };
    
       return result;
    
    };

    然后,继续下去,仍然一次替换一个检查动作:

    double getPayAmount() {
    
       double result;
    
       if (_isDead) return deadAmount();
    
       if (_isSeparated) return separatedAmount();
    
       if (_isRetired) result = retiredAmount();
    
       else result = normalPayAmount();
    
       return result;
    
    };

    然后是最后一个:

    double getPayAmount() {
    
       double result;
    
       if (_isDead) return deadAmount();
    
       if (_isSeparated) return separatedAmount();
    
       if (_isRetired) return retiredAmount();
    
       result = normalPayAmount();
    
       return result;
    
    };

    此时,result 变量已经没有价值了,所以我把它删掉:

    double getPayAmount() {
    
       if (_isDead) return deadAmount();
    
       if (_isSeparated) return separatedAmount();
    
       if (_isRetired) return retiredAmount();
    
      return normalPayAmount();
    
    };

    嵌套(nested)条件代码往往由那些深信「每个函数只能有一个出口」的程序员写出。Martin Fowler发现那条规则(函数只能有一个出口)实在有点太简单化了。如果对函数剩余部分不再有兴趣,当然应该立刻退出。引导阅读者去看一个没有用的else 区段,只会妨碍他们的理解

    范例:将条件逆反(Reversing the Conditions)

    public double getAdjustedCapital() {
    
        double result = 0.0;
    
        if (_capital > 0.0) {
    
          if (_intRate > 0.0 && _duration > 0.0) {
    
            result = (_income / _duration) * ADJ_FACTOR;
    
          }
    
        }
    
        return result;
    
      }

    同样地,我逐一进行替换。不过这次在插入卫语句(guard clauses)时,我需要将相应的条件逆反过来:

    public double getAdjustedCapital() {
    
           double result = 0.0;
    
        if (_capital <= 0.0) return result;
    
           if (_intRate > 0.0 && _duration > 0.0) {
    
             result = (_income / _duration) * ADJ_FACTOR;
    
           }
    
           return result;
    
       }

    下一个条件稍微复杂一点,所以我分两步进行逆反。首先加入一个"logical-NOT"操作

     public double getAdjustedCapital() {
    
          double result = 0.0;
    
          if (_capital <= 0.0) return result;
    
        if (!(_intRate > 0.0 && _duration > 0.0)) return result;
    
          result = (_income / _duration) * ADJ_FACTOR;
    
          return result;
    
      }

    但是在这样的条件式中留下一个"logical-NOT",会把我的脑袋拧成一团乱麻,所以我把它简化成下面这样:

     public double getAdjustedCapital() {
    
          double result = 0.0;
    
          if (_capital <= 0.0) return result;
    
          if (_intRate <= 0.0 || _duration <= 0.0) return result;
    
          result = (_income / _duration) * ADJ_FACTOR;
    
          return result;
    
      }

    这时候我比较喜欢在卫语句(guard clause)内返回一个明确值,因为这样我可以一 目了然地看到卫语句返回的失败结果。此外,这种时候我也会考虑使用Replace Magic Number with Symbolic Constant 。

     public double getAdjustedCapital() {
    
          double result = 0.0;
    
          if (_capital <= 0.0) return 0.0;
    
          if (_intRate <= 0.0 || _duration <= 0.0) return 0.0;
    
          result = (_income / _duration) * ADJ_FACTOR;
    
          return result;
    
      }

    完成替换之后,我同样可以将临时变量移除:

    public double getAdjustedCapital() {
    
          if (_capital <= 0.0) return 0.0;
    
          if (_intRate <= 0.0 || _duration <= 0.0) return 0.0;
    
        return (_income / _duration) * ADJ_FACTOR;
    
      }

    引用 重构改善代码的既有设计

  • 相关阅读:
    【JZOJ5771】遨游【二分】【DFS】
    【JZOJ5773】简单数学题【数论,数学】
    【JZOJ5773】简单数学题【数论,数学】
    有效壳第2部分:成为一个剪贴板体操运动员
    具有多重选择和列表间拖拽的拖拽列表框
    将枚举绑定到下拉列表框并根据值对其排序
    一个具有子项格式的自定义绘制列表控件
    基本的c#屏幕截图应用程序
    将组合框下拉列表宽度调整为最长字符串宽度
    在应用程序中使用按钮控件
  • 原文地址:https://www.cnblogs.com/newbee0101/p/11982077.html
Copyright © 2020-2023  润新知