函数中的条件逻辑使人难以看清正常的执行路径。使用卫语句表现所有特殊情况
根据我的经验,条件式通常有两种呈现形式。第一种形式是:所有分支都属于正常行为。第二种形式则是:条件式提供的答案中只有一种是正常行为,其他都是不常见的情况。
这两类条件式有不同的用途,这一点应该通过代码表现出来。如果两条分支都是正常行为,就应该使用形如「if…then…」的条件式;如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回。这样的单独检查常常被称为卫语句(guard clauses)
Replace Nested Conditional with Guard Clauses 的精髓就是:给某一条分支以特别的重视。如果使用if-then-else 结构,你对if 分支和else 分支的重视是同等的。 这样的代码结构传递给阅读者的消息就是:各个分支有同样的重要性。
卫语句(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() {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();};
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;}
下一个条件稍微复杂一点,所以我分两步进行逆反。首先加入一个逻辑非操作
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;if (_intRate <= 0.0 || _duration <= 0.0) return result;result = (_income / _duration) * ADJ_FACTOR;return result;}
这时候,我比较喜欢在卫语句内返回一个明确值,因为这样我可以一目了然地看到卫语句返回的失败结果。此外,这种时候我也会考虑使用Replace Magic Number with Symbolic Constant (204)。
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;}
上一篇:如何更优雅的对接第三方API
下一篇:每日学术速递3.22