zoukankan      html  css  js  c++  java
  • Replace Nested Conditional with Guard Clauses(用卫语句代替嵌套循环)

    函数中的条件逻辑,使人难以看清正常的执行路径。

    使用卫语句表现所有特殊情况。

    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() {
    if (_isDead) return deadAmount();
    if (_isSeparated) return separatedAmount();
    if (_isRetired) return retiredAmount();
    return normalPayAmount();
    };

    动机

    根据我的经验,条件式通常有两种呈现形式。第一种形式是所有分支都属于正常行为,第二种形式则是条件式提供的答案只有一种是正常行为,其他都是不常见的情况。

    这两类条件式有不同的用途,这一点应该通过代码表现出来。如果两条分支都是正常行为,就应该使用形如[if...else...]的条件式;如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回,这样的单独检查常常被称为[卫语句]。

    Replace Nested Conditional with Guard Clauses的精髓就是给某一条分支以特别的重视,如果使用if-then-else结构,你对if分支和else分支重视是同等的。这样的代码结构传递给阅读者的消息就是:各个分支有同样的重要性,卫语句就不同了,它告诉读者,这种情况很罕见,如果它真的发生了,请做一些必要的整理工作,然后退出。

    每个函数只能有一个入口和一个出口的观念,根深蒂固与某些程序员的脑海里。我发现,当我处理他们编写的代码时,我经常需要使用Replace Nested Conditional with Guard Clauses。现在的编程语言都会强制保证每个函数只有一个入口,至于单一出口规则,其实不是那么有用。在我看来保持代码清晰才是最关键的。如果[单一出口]能使这个函数更清楚易读,那么就使用单一出口;否则就不必这么做。

    作法

    1. 对于每个检查,放进一个卫语句

    卫语句要不就从函数中返回,要不就抛出一个异常。

    2. 每次将[条件检查]替换成[卫语句]后,编译并测试。

    如果所有卫语句都导致相同结果,请使用Consolidate Conditional Expressions。

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

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

    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();
    };

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

    将条件逆反

    你常常可以将条件表达式逆反,从而实现Replace Nested Conditional with Guard Clauses。请看下面的例子:

    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;
    }

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

    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;
    }

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

    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;
    }

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

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

    这时,我比较喜欢在卫语句内返回一个明确值。因为这样我可以一目了然的看到卫语句返回的失败结果。此外,这时候我也会考虑使用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;
    }

  • 相关阅读:
    关于ugc的一点思考
    Fenng早年间对推荐系统的思考
    对于软件开发的一些思考
    并发排序
    Standford CoreNLP使用
    做事情的方式
    JAVA! static的作用
    struts2学习笔记--使用Validator校验数据
    Struts2中的ModelDriven机制及其运用
    ValueStack与ContentMap (ActionContext.getContext().getValueStack().set())
  • 原文地址:https://www.cnblogs.com/sprinng/p/3473204.html
Copyright © 2011-2022 走看看