条件逻辑有可能十分复杂,因此本章提供一些重构手法,专门用来简化它们。其中一项核心重构就是Decompose Conditional,可将一个复杂的条件逻辑分成若干小块。这项重构很重要,因为它使逻辑和操作细节的分离。
本章的其余重构手法可用以处理另一些重要问题,如果你发现代码中的多处测试有相同的结构,应该实施Consolidate Conditional Expression,如果条件代码中有任何重复,可以运用Consolidate Duplicate Conditional Fragment 将重复成分去掉。
较之于过程化程序而言,面向对象因为多态的机制可以简化条件行为的细节
1. Replace Nested Condition with Guard Clauses(以卫语句
取代嵌套条件表达式)
Clauses[klɔ:zis] :字句 Guard[ga:d]: 守卫、看守 Nesting: 嵌套
范例:
想象一个薪酬系统,其中以特区规则处理死亡、驻外、退休员工的薪资,这些情况不常有,但偶然会出现
常规编码:
1: private double getPlayAmount(){
2: double result;
3: if(_isDead){
4: result = deadAmount();
5: }else{
6: if(_isSeparated){
7: result = separatedAmount();
8: }else {
9: if(_isRetired){
10: result = retiredAmount();
11: }else {
12: result = normalAmount();
13: }
14: }
15: }
16: return result;
17: }
分析: 条件表达式通常有两种表现形式。第一种:所有的分支都属于正常行为,应该用
if..else表达出来。第二种:条件表达式提供的答案中只有一种是正常行为,其
他都是不常见的行为。此时就要单独检查该条件,并在该条件为真时立刻从函数
中返回,这种单独检测常常为称为“卫语句”通过分析题意,我们知道该函数主
体是付普通情况的工资,而死亡、驻外、退休属于特区情况,因此我们把他们做
为卫语句处理,修改后为:
1: private double getPlayAmount(){
2:
3: if(_isDead){
4: return deadAmount();
5: }
6:
7: if(_isSeparated){
8: return separatedAmount();
9: }
10:
11: if(_isRetired){
12: return retiredAmount();
13: }
14:
15: return normalAmount();
16: }
2. Decompose Conditional (分解条件表达式)
decompose [ˌdi:kəmˈpəuz] :分解、腐烂
-----你有一个复杂的条件语句(if、then、else),从if ,then,else三个段落中分别提炼出独立函数
假设我要计算购买某样商品的总价(总价=数量 * 单价),而这个商品在冬季和夏季的单价是不同的。
if(data.before(SUMMER_START) || data.after(SUMMER_END)){ charge = quantity * _winterRate ; }else{ charge = quantity * _summerRate }
分析:复杂的条件逻辑是最常导致复杂度上升的原因之一,常见的做法是1) 将if段提炼出来,构成一个独立的函数 2)将then和else段提炼出来构成一个独立的函数
修改后的代码为:
if(notSummer(data)){ charge = winterCharge(quantity); }else{ charge = summerCharge(quantity); } private boolean notSummer(Data data){ return data.before(SUMMER_START) || data.after(SUMMER_END); } private double summerCharge(int quantity){ return quantity * _summerRate; } private double winterCharge(int quantity){ return quantity * _winterRate; }
通过以上的简化,条件表达式逻辑块就像代码注释一样清晰、明了
3. Consolidate Conditional Expression (合并条件表达式)
----你有一系列的条件测试,都得到相同的结果,将这些测试合并为一个条件表达式,并将这个条件表达式提炼为一个独立的函数
示例:
double disabilityAmount(){ if(_seniority < 2){ return 0; } if(_months > 12){ return 0; } if(_isPartTime){ return 0; } }
分析:有时候你会发现一连串的检查条件,虽然条件各不相同,但是最终行为却一致,这种情况一般是用“逻辑与”或者“逻辑或”把他们合并为一个条件表达式,使代码更清楚。 合并后
double disabilityAmount(){ if(isNotAbility()){ return 0 } } boolean isNotAbility(){ return ((_seniority < 2) || (_months > 12) || _isPartTime) }
4. Consolidate Duplicate Conditional Fragements(合并重复的
条件片段)
---在条件表达式的每个分支上有着相同的一段代码
if(isBook){ total = price * 0.95; send(); }else{ total = price * 0.5; send(); }
分析: 针对这种情况,应该将重复的代码搬移到条件表达式的外面,这样代码才能够更加清
楚的表明那些东西随条件的变化而变化,那些东西保持不变
if(isBook){ total = price * 0.95; }else{ total = price * 0.5; } send();
5. Replace Conditional with Polymorphism(以多态取代条件
表达式)
如果需要根据对象的不同类型而采取不同的行为,多态使你不需要编写明显的条件表达式。比如重构第一章我们提到的电影分儿童、新片、普通片。这三种片都是电影的不同类型,我们可以定义一个电影的抽象类,然后利用多态的性质,让不同的类型的电影去继承,这样我们就可以去掉多余的条件判断