• 如何以卫语句取代嵌套条件表达式?


    摘要:以卫语句取代嵌套条件表达式的精髓就是:给某一条分支以特别的重视。

    本文分享自华为云社区《以卫语句取代嵌套条件表达式 (Replace Nested Conditional with Guard Clauses)》,作者:JavaEdge。

    动机

    条件表达式通常有两种风格:

    • 两个条件分支都属于正常行为
    • 只有一个条件分支是正常行为,另一个分支是异常情况

    这两类条件表达式有不同用途,这一点应该通过代码表现出来:

    • 若两条分支都是正常行为,就应该使用形如if…else…的条件表达式
    • 若某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回

    这样的单独检查常常被称为“卫语句”(guard clauses)

    以卫语句取代嵌套条件表达式的精髓就是:给某一条分支以特别的重视。如果使用if-then-else结构,你对if分支和else分支的重视是同等的。这样的代码结构传递给阅读者的消息就是:各个分支有同样的重要性。而卫语句是在告诉读者:“这种情况不是本函数的核心逻辑所关心的,如果它真发生了,请做一些必要的整理工作,然后退出。”

    “每个函数只能有一个入口和一个出口”的观念根深蒂固于某些程序员。当我处理他们编写的代码时,经常需要使用以卫语句取代嵌套条件表达式。现今编程语言都会强制保证每个函数只有一个入口,至于“单一出口”规则,其实不那么有用。保持代码清晰才是最关键的:若单一出口能使这个函数更清楚易读,那就使用单一出口;否则不必。

    做法

    选中最外层需要被替换的条件逻辑,将其替换为卫语句。

    测试。

    有需要的话,重复上述步骤。

    如果所有卫语句都引发同样的结果,可以使用【合并条件表达式】合并之。

    案例

    计算要支付给员工的工资。只有还在公司上班的员工才需要支付工资,所以这个函数需要检查两种“员工已经不在公司上班”的情况。

    public Long payAmount(Employee employee) {
            long result;
            if (employee.isSeparated) {
                result = 0;
            } else {
                if (employee.isRetired) {
                    result = 0;
                } else {
                    // logic to compute amount
                    lorem.ipsum(dolor.sitAmet);
                    consectetur(adipiscing).elit();
                    sed.do.eiusmod = tempor.incididunt.ut(labore) && dolore(magna.aliqua);
                    ut.enim.ad(minim.veniam);
                    result = someFinalComputation();
                }
            } return result;
        }

    嵌套的条件逻辑让我们看不清代码真实的含义。只有当前两个条件表达式都不为真的时候,这段代码才真正开始它的主要工作。所以,卫语句能让代码更清晰地阐述自己的意图。一如既往地,我喜欢小步前进,所以我先处理最顶上的条件逻辑。

     public Long payAmount(Employee employee) {
            long result;
            if (employee.isSeparated) {
                result = 0;
            }
            if (employee.isRetired) {
                result = 0;
            } else { // logic to compute amount
                lorem.ipsum(dolor.sitAmet);
                consectetur(adipiscing).elit();
                sed.do.eiusmod = tempor.incididunt.ut(labore) && dolore(magna.aliqua);
                ut.enim.ad(minim.veniam);
                result = someFinalComputation();
            } return result;
        }

    做完这步修改,我执行测试,然后继续下一步。

     public Long payAmount(Employee employee) {
            long result;
            if (employee.isSeparated) {
                return 0l;
            }
            if (employee.isRetired) {
                return 0l;
            }
    
            lorem.ipsum(dolor.sitAmet);
            consectetur(adipiscing).elit();
            sed. do.eiusmod = tempor.incididunt.ut(labore) && dolore(magna.aliqua);
            ut.enim.ad(minim.veniam);
            result = someFinalComputation();
            return result;
        }

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

     public Long payAmount(Employee employee) {
            if (employee.isSeparated) {
                return 0l;
            }
            if (employee.isRetired) {
                return 0l;
            }
            lorem.ipsum(dolor.sitAmet);
            consectetur(adipiscing).elit();
            sed. do.eiusmod = tempor.incididunt.ut(labore) && dolore(magna.aliqua);
            ut.enim.ad(minim.veniam);
            return someFinalComputation();
        }

    能减少一个可变变量总是好的。

    将条件反转

    我们常常可以将条件表达式反转,从而实现以卫语句取代嵌套条件表达式。

    public int adjustedCapital(Instrument anInstrument) {
      int result = 0;
      if (anInstrument.capital > 0) {
        if (anInstrument.interestRate > 0 && anInstrument.duration > 0) {
          result = (anInstrument.income / anInstrument.duration) * anInstrument.adjustmentFactor;
        }
      }
      return result;
    }

    逐一替换。但这次在插入卫语句时, 我需要将相应的条件反转过来:

    public int adjustedCapital(Instrument anInstrument) {
      int result = 0;
      if (anInstrument.capital <= 0) {
        return result;
      }
      if (anInstrument.interestRate > 0 && anInstrument.duration > 0) {
        result = (anInstrument.income / anInstrument.duration) * anInstrument.adjustmentFactor;
      }
      return result;
    }

    下一个条件稍微复杂一点,所以我分两步进行反转。首先加入一个逻辑非操作:

    public int adjustedCapital(Instrument anInstrument) {
      int result = 0;
      if (anInstrument.capital <= 0) {
        return result;
      }
      if (!(anInstrument.interestRate > 0 && anInstrument.duration > 0)) {
        return result;
      }
      result = (anInstrument.income / anInstrument.duration) * anInstrument.adjustmentFactor;
      return result;
    }

    但是在这样的条件表达式中留下一个逻辑非,会把我的脑袋拧成一团乱麻,所以我把它简化成:

    public int adjustedCapital(Instrument anInstrument) {
      int result = 0;
      if (anInstrument.capital <= 0) {
        return result;
      }
      if (anInstrument.interestRate <= 0 || anInstrument.duration <= 0) {
        return result;
      }
      result = (anInstrument.income / anInstrument.duration) * anInstrument.adjustmentFactor;
      return result;
    }

    这两行逻辑语句引发的结果一样,所以我可以用【合并条件表达式】将其合并:

    public int adjustedCapital(Instrument anInstrument) {
      int result = 0;
      if (anInstrument.capital <= 0 || anInstrument.interestRate <= 0 || anInstrument.duration <= 0) {
        return result;
      }
      result = (anInstrument.income / anInstrument.duration) * anInstrument.adjustmentFactor;
      return result;
    }

    此时result变量做了两件事:一开始我把它设为0,代表卫语句被触发时的返回值;然后又用最终计算的结果给它赋值。我可以彻底移除这个变量,避免用一个变量承担两重责任,而且又减少了一个可变变量。

    public int adjustedCapital(Instrument anInstrument) {
      if (anInstrument.capital <= 0 || anInstrument.interestRate <= 0 || anInstrument.duration <= 0) {
        return 0;
      }
      return (anInstrument.income / anInstrument.duration) * anInstrument.adjustmentFactor;
    }

    参考

    • 《重构》
    • 《架构整洁之道》

     

    点击关注,第一时间了解华为云新鲜技术~

  • 相关阅读:
    java获取web项目下文件夹的路径方法
    The method setCharacterEncoding(String) is undefined for the type HttpServletResponse
    java获取windows和linux下本机ip通用方法
    mysql慢查询日志查找与分析
    struts1 action之间的跳转
    jquery的tap会执行2次的替换办法
    Win7下如何安装切换jdk7和jdk8
    elasticdump
    python hive.py
    Hdfs数据备份
  • 原文地址:https://www.cnblogs.com/huaweiyun/p/16116853.html
Copyright © 2020-2023  润新知