《重构改善既有代码的设计》之重构列表--简化条件表达式(一)

一、Decompose Conditional(分解条件表达式)

你有一个复杂的条件(if-then-else)语句。

ifthenelse三个段落中分别提炼出独立函数。

If(date.before(SUMMER_START) || date.after(SUMMER_END)){

charge = quantity * _winterRate + _winterServiceCharge;

}else{

charge = quantity * _summerRate;

}

转换后:

If(notSummer(date))

charge = winterCharge(quantity);

Else charege = summerCharge(quantity);

动机

程序之中,复杂的条件逻辑是最常导致复杂度上升的地点之一。你必须编写代码来检查不同的条件分支、根据不同的分支做不同的事情,然后,你很快就会得到一个想当长的函数。大型函数自身就会使代码的可读性降低,而条件逻辑则会使代码更难阅读。在带有复杂条件逻辑的代码中,代码(包括检查条件分支的代码和真正的实现功能的代码)会告诉你发生的事,但常常你弄不清楚为什么会发生这样的事,这就说明代码的可读性的确大大降低了。

和任何大块头代码一样,你可以将它分解为多个独立函数,根据每个小块代码的用途,为分解而得的新函数命名,并将原函数中对应的代码改为调用新建函数,从而更清楚的表达自己的意图。对于条件逻辑,将每一个分支条件分解成新函数还可以给你带来更多好处:可以突出条件逻辑,更清楚地表明每个分支的作用,并且突出每个分支的原因。

做法

1、if段落提炼出来,构成一个独立函数。

2、then段落和else段落都提炼出来,各自构成一个独立函数。

如果发现嵌套的条件逻辑,我通常会先观察是否可以使用Replace Nested Conditional with Guard Clauses。如果不行,才开始分居其中的每个条件。

二、Consolidate Conditional Expression(合并条件表达式)

你有一系列条件测试,都得到相同结果。

将这些测试合并为一个条件表达式,并将这个条件表达式提炼成为一个独立函数。

Double DisabilityAmount(){

If(_seniority < 2) return 0;

If(_monthDisabled > 12) return 0;

If(_isPartTime) return 0;

}

转换后:

Double disabilityAmount(){

If(isNotEligibleForDisability()) return 0;

}

动机

有时你会发现这样一串条件检查:检查条件各不相同,最终行为却一致。如果发现这种情况,就应该使用“逻辑或”和“逻辑与”将它们合并为一个条件表达式。

之所以要合并条件代码,有两个重要原因。首先,合并后的条件代码会告诉你“实际上只有一次条件检查,只不多有多个并列条件需要检查而已”,从而使这一次检查的用意更清晰。当然,合并前后的代码有相同的效果,但原先代码传达出的信息是“这里有一些各自独立的条件测试,它们只是恰好同时发生”。其次,这项重构往往可以为你运用Extract Method做好准备。将检查条件提炼成一个独立函数对于厘清代码意义非常有用,因为它把描述“做什么”的语句换成了“为什么这样做”。

条件语句的合并理由也同时指出了不要合并的理由:如果你认为这些检查确实彼此独立,的确不应该被视为同一次检查,那么就不要使用本重构。因为在这种情况下,你的代码已经清楚的表达出自己的意义。

做法

1、确定这些条件语句都没有副作用。

2、使用适当的逻辑操作符,将一系列相关条件表达式合并为一个。

3、编译、测试

4、对合并后的条件表达式实施Extract Method

三、Consolidate Duplicate Conditional Fragments(合并重复的条件片段)

在条件表达式的每个分支上有着相同的一段代码。

将这段重复代码搬移到条件表达式之外。

If(isSpecialDeal()){

Total = price * 0.95;

Send();

}else{

Total = price * 0.98;

Send();

}

转换后:

If(isSpecialDeal()){

Total = price * 0.95;

}else{

Total = price * 0.98;

}

Send();

动机

有时你会发现,一组条件表达式的所有分支都执行了相同的某段代码。如果是这样,你就应该将这段代码搬移到条件表达式外面,这样,代码才能更清楚的表明哪些东西随条件的变化而变化,哪些东西保持不变。

做法

1、鉴别出“执行方式不随条件变化而变化”的代码

2、如果这些共同代码位于条件表达式起始处,就将它搬移到条件表达式之前。

3、如果这些共同代码位于条件表达式尾端,就将它搬移到条件表达式之后。

4、如果这些共通代码位于条件表达式中段,就需要观察共通代码之前或之后的代码是否改变了什么东西。如果的确有所改变,应该首先将共通代码向前或向后移动,移至条件表达式的起始处或尾端,再以前面所说的办法来处理。

5、如果共通代码不止一处,应该首先使用Extract Method将共通代码提炼到一个独立函数中,再以前面的办法来处理。

四、Remove Control Flag(移除控制标记)

在一系列布尔表达式中,某个变量带有“控制标记”的作用。

breakreturn取代控制标记。

动机

在一系列条件表达式中,你常常会看到用以判断何时停止条件检查的控制标记:

Set done to false

While no done 

If(condition)

Do something

Set done to true

Next step of loop

这样的控制标记带来的麻烦超过了它所带来的便利。人们之所以会使用这样的控制标记,因为结构化编程告诉我们:每个子程序只能有一个入口和一个出口。我赞同“统一入口”原则(而且现代编程语言也强迫我们这样做),但是“单一出口”原则会让你在代码中加入讨厌的控制标记,大大降低条件表达式的可读性。这就是编程语言提供break语句和return语句的原因:用它们跳出复杂的条件语句。去掉控制标记所产生的效果往往会让你大吃一惊:条件语句真正的用途会清晰得多。

做法

对控制标记的处理,最显而易见的办法就是Java提供的break语句或continue语句。

1、找出让你跳出这段逻辑的控制标记值。

2、找出对标记变量赋值的语句,代以恰当的break语句或continue语句。

3、每次替换后,编译并测试。

在未能提供breakcontinue的编程语言中,可以使用下述办法。

1、运用Extract Method,将整段逻辑提炼到独立函数中。

2、找出让你跳出这段逻辑的控制标记值。

3、找出对标记变量赋值的语句,代以恰当的return语句。

4、每次替换后,编译并测试。

即使在支持breakcontinue语言中,我通常也优先考虑上述第二种方案。因为return语句可以非常清楚的表示:不再执行该函数中的其他任何代码。如果还有这一类代码,你早晚需要将这段代码提炼出来。

请注意标记变量是否会影响这段逻辑的最后结果。如果有影响,使用break之后还得保留控制标记值。如果你已经将这段代码逻辑提炼成一个独立函数,也可以将控制标记值放在return中返回。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值