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 ...
随机推荐
- cowboy-高性能简洁的erlang版web框架
那么Cowboy是什么呢? Cowboy is a small, fast and modular HTTP server written in Erlang. 其定位非常明确: Cowboy aim ...
- jQuery的查找
children([expr])概述 :取得一个包含匹配的元素集合中每一个元素的所有子元素的元素集合.可以通过可选的表达式来过滤所匹配的子元素.注意:parents()将查找所有祖辈元素,而child ...
- <jsp:invoke fragment=""/>的理解和使用
在传统 JSP 中,想要实现页面布局管理比较麻烦,为了解决在 JSP 中布局的问题,出现了很多开源软件,比如 Apache Tiles 和 SiteMesh 就是其中比较优秀的.但是使用开源软件实现布 ...
- EasyUI queryParams属性 在请求远程数据同时给action方法传参
http://www.cnblogs.com/iack/p/3530500.html?utm_source=tuicool EasyUI queryParams属性 在请求远程数据同时给action方 ...
- linux下python版webshell后门查杀工具
使用说明: 1.查杀指定路径:python webshell.py 路径 2.按时间查找文件:python webshell.py 路径 “2013-09-28 00:00:00″ # -*- cod ...
- CSS打造经典鼠标触发显示选项
650) this.width=650;" border="0" alt="" src="http://img1.51cto.com/att ...
- derby数据库操作比较难理解的错误及解决方法大全
一.插入(INSERT时报错) 1.错误:java.sql.SQLIntegrityConstraintViolationException: 列“test”无法接受空值. 可能原因:建表时test列 ...
- Ubuntu 中软件的安装、卸载以及查看的方法总结
Ubuntu 中软件的安装.卸载以及查看的方法总结 博客分类: Linux UbuntuDebian配置管理CacheF# 说明:由于图形化界面方法(如Add/Remove... 和Synaptic ...
- C#比较时间大小 1、比较时间大小的实验
1.比较时间大小的实验 string st1="12:13"; string st2="14:14"; DateTime dt1=Convert.ToDa ...
- zoj.3865.Superbot(bfs + 多维dp)
Superbot Time Limit: 2 Seconds Memory Limit: 65536 KB Superbot is an interesting game which you ...