条件逻辑有可能十分复杂,因此本章提供一些重构手法,专门用来简化它们。其中一项核心重构就是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(以多态取代条件
表达式)
如果需要根据对象的不同类型而采取不同的行为,多态使你不需要编写明显的条件表达式。比如重构第一章我们提到的电影分儿童、新片、普通片。这三种片都是电影的不同类型,我们可以定义一个电影的抽象类,然后利用多态的性质,让不同的类型的电影去继承,这样我们就可以去掉多余的条件判断