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;
}
Replace Nested Conditional with Guard Clauses(用卫语句代替嵌套循环)的更多相关文章
- 控制结构(2) 卫语句(guard clause)
// 上一篇:分枝/叶子(branch/leaf) // 下一篇:状态机(state machine) 基于语言提供的基本控制结构,更好地组织和表达程序,需要良好的控制结构. 典型代码: 同步版本 f ...
- 控制结构(2): 卫语句(guard clause)
// 上一篇:分枝/叶子(branch/leaf) // 下一篇:状态机(state machine) 基于语言提供的基本控制结构,更好地组织和表达程序,需要良好的控制结构. 典型代码: 同步版本 f ...
- java - 策略模式、状态模式、卫语句,避免多重if-else(转)
前言 当代码中出现多重if-else语句或者switch语句时.弊端之一:如果这样的代码出现在多处,那么一旦出现需求变更,就需要把所有地方的if-else或者switch代码进行更改,要是遗漏了某一处 ...
- Java重构-策略模式、状态模式、卫语句
前言 当代码中出现多重if-else语句或者switch语句时.弊端之一:如果这样的代码出现在多处,那么一旦出现需求变更,就需要把所有地方的if-else或者switch代码进行更改,要是遗漏了某一处 ...
- 【转】Java重构-策略模式、状态模式、卫语句
前言 当代码中出现多重if-else语句或者switch语句时.弊端之一:如果这样的代码出现在多处,那么一旦出现需求变更,就需要把所有地方的if-else或者switch代码进行更改,要是遗漏了某一处 ...
- MySQL Block Nested Loop and Batched Key Access Joins(块嵌套循环和批量Key访问连接)
Block Nested-Loop and Batched Key Access Joins Batched Key Access (BKA) Join算法通过index和join buffer访问j ...
- 重构 改善既有代码的设计 (Martin Fowler 著)
第1章 重构, 第一个案例 1.1 起点 1.2 重构的第一步 1.3 分解并重组 statement() 1.4 运用多态取代与价格相关的条件逻辑 1.5 结语 第2章 重构原则 2.1 何谓重构 ...
- 《重构》中Tips总结
1 如果你发现自己需要为程序添加一个特性,而代码结构使你无法很方便地达到目的,那就先重构那个程序,使特性的添加比较容易进行,然后再添加特性. 2 重构之前,首先检查自己 ...
- Simplifying Conditional Expressions(简化条件表达式)
1.Decompose Conditional(分解条件表达式) 2.Consolidate Conditional Expressions(合并条件表达式) 3.Consolidate Duplic ...
随机推荐
- POJ 2481Cows(树状数组 + 好题)
Cows Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 15222 Accepted: 5070 Description ...
- easyloader.js源代码分析
http://www.cnblogs.com/jasonoiu/p/easyloader_source_code_analysis.html Jquery easyui是一个javascript UI ...
- JS小记
好记性不如烂笔头. 1.document.ElementFromPoint:根据坐标获得元素 2.有时候要操作DOM页面,但是得不到预期结果,很可能是因为页面还没加载完成,在console控制台可以看 ...
- N个数全排列的非递归算法
//N个数全排列的非递归算法 #include"stdio.h" void swap(int &a, int &b) { int temp; temp = a; a ...
- firefox访问失败的时间设置错误问题
在新装系统, 安装firefox后, 访问网页: baidu时 总是自动将http转换为https, 这个是baidu服务器的设置问题, 怪不到ff bd说,是ocsp证书错误, 然后将ocsp认证q ...
- 每日一练(写不出心得体会了!毕竟哪有那么多心得好写。然后看github上有很多不错的题目。分享一下!)
第一题: 问题描述:写一个reverseWords函数 调用方式:console.log(reverseWords('Hello World')); 期望输出:World Hello 第二题: 问题描 ...
- 一张图解释Hadoop IPC
基于hadoop2.6.2.... 一张图Server启动,Client访问..... RPC是IPC的一种,IPC还有另外一种LPC,相关请看参考中的3 使用hadoop ipc步骤: 1.定义RP ...
- Drainage Ditches(dinic)
Drainage Ditches Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 59210 Accepted: 2273 ...
- CKEditor使用笔记
相关资源 1. 首页地址:http://ckeditor.com/ 2. 下载地址:http://ckeditor.com/download 3. SDK地址:http://sdk.ckeditor. ...
- invert
http://docs.ruby-lang.org/en/2.0.0/Hash.html invert → new_hash Returns a new hash created by using h ...