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

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

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(用卫语句代替嵌套循环)的更多相关文章

  1. 控制结构(2) 卫语句(guard clause)

    // 上一篇:分枝/叶子(branch/leaf) // 下一篇:状态机(state machine) 基于语言提供的基本控制结构,更好地组织和表达程序,需要良好的控制结构. 典型代码: 同步版本 f ...

  2. 控制结构(2): 卫语句(guard clause)

    // 上一篇:分枝/叶子(branch/leaf) // 下一篇:状态机(state machine) 基于语言提供的基本控制结构,更好地组织和表达程序,需要良好的控制结构. 典型代码: 同步版本 f ...

  3. java - 策略模式、状态模式、卫语句,避免多重if-else(转)

    前言 当代码中出现多重if-else语句或者switch语句时.弊端之一:如果这样的代码出现在多处,那么一旦出现需求变更,就需要把所有地方的if-else或者switch代码进行更改,要是遗漏了某一处 ...

  4. Java重构-策略模式、状态模式、卫语句

    前言 当代码中出现多重if-else语句或者switch语句时.弊端之一:如果这样的代码出现在多处,那么一旦出现需求变更,就需要把所有地方的if-else或者switch代码进行更改,要是遗漏了某一处 ...

  5. 【转】Java重构-策略模式、状态模式、卫语句

    前言 当代码中出现多重if-else语句或者switch语句时.弊端之一:如果这样的代码出现在多处,那么一旦出现需求变更,就需要把所有地方的if-else或者switch代码进行更改,要是遗漏了某一处 ...

  6. 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 ...

  7. 重构 改善既有代码的设计 (Martin Fowler 著)

    第1章 重构, 第一个案例 1.1 起点 1.2 重构的第一步 1.3 分解并重组 statement() 1.4 运用多态取代与价格相关的条件逻辑 1.5 结语 第2章 重构原则 2.1 何谓重构 ...

  8. 《重构》中Tips总结

    1         如果你发现自己需要为程序添加一个特性,而代码结构使你无法很方便地达到目的,那就先重构那个程序,使特性的添加比较容易进行,然后再添加特性. 2         重构之前,首先检查自己 ...

  9. Simplifying Conditional Expressions(简化条件表达式)

    1.Decompose Conditional(分解条件表达式) 2.Consolidate Conditional Expressions(合并条件表达式) 3.Consolidate Duplic ...

随机推荐

  1. android 自定义控件 使用declare-styleable进行配置属性(源码角度)

          android自定义styleableattrs源码 最近在模仿今日头条,发现它的很多属性都是通过自定义控件并设定相关的配置属性进行配置,于是便查询了解了下declare-styleabl ...

  2. Xcode的一些有用的插件

    ** --Alcatraz:Xcode插件管理  ** 安装:curl -fsSL https://raw.github.com/supermarin/Alcatraz/master/Scripts/ ...

  3. python 与 mysql

    1.开发环境: 1)CLion-2016.1.3 C/C++ 与 Python 混合编程 IDE,先安装好以下 2) 3) 编译器再关联 2)tdm-gcc-4.8.1-3 C/C++ 编译器 3)W ...

  4. 二、Ubuntu14.04下安装Hadoop2.4.0 (伪分布模式)

    在Ubuntu14.04下安装Hadoop2.4.0 (单机模式)基础上配置 一.配置core-site.xml /usr/local/hadoop/etc/hadoop/core-site.xml ...

  5. 设置button不同状态下的背景色,即把这个颜色变成图片设置成,背景图片

    - (void)setBackgroundColor:(UIColor *)backgroundColor forState:(UIControlState)state { [self setBack ...

  6. 10GE---超长距离的万兆以太网

    万兆以太网 锁定 本词条由“科普中国”百科科学词条编写与应用工作项目 审核 . 以太网标准是一个古老而又充满活力的标准.自从1982年以太网协议被IEEE采纳成为标准以后,已经历了20年的风风雨雨.在 ...

  7. smarty中math函数的用法

    在查看项目代码时,发现smart代码中有这样写. {if $auctionInfo.printingNum} 印数:{math equation="cid/1000" cid=$a ...

  8. Flume-NG(1.5版本)中SpillableMemoryChannel源码级分析

    SpillableMemoryChannel是1.5版本新增的一个channel.这个channel优先将evnet放在内存中,一旦内存达到设定的容量就使用file channel写入磁盘.然后读的时 ...

  9. java笔记--关于线程死锁

    关于线程死锁 什么是死锁: 在编写多线程的时候,必须要注意资源的使用问题,如果两个或多个线程分别拥有不同的资源, 而同时又需要对方释放资源才能继续运行时,就会发生死锁. 简单来说:死锁就是当一个或多个 ...

  10. 存在使i > j || i <= j不成立的数吗?

    存在使i > j || i <= j不成立的数吗? 咋一看有点晕!一个数既不能大于也不能小于等于另一个数?那是什么数?答案是”非数“ 例子如下:‘ if(Double.NaN>Flo ...