重构 - 10 简化条件逻辑
10 简化条件逻辑
分解条件表达式
对条件判断和每个条件分支分别运用提炼函数手法。
将大块头代码分解为多个独立的函数,并根据每个小块代码的用途,为分解得到的新函数命名。
重构前:
if (!aData.isBefore(plan.summerStart) && !aData.isAfter(plan.summerEnd)) {
// 计算夏季总价
charge = quantity * plan.summerRate;
} else {
// 计算平时总价
charge = quantity * plan.regularRate + plan.regularServiceCharge;
}
重构后:
if (summer()) {
charge = summerCharge();
} else {
charge = regularCharge();
}
charge = summer() ? summerCharge() : regularCharge();
合并条件表达式
当检查条件各不相同,但最终行为却一致时,应该使用 “逻辑或” 和 “逻辑与” 将它们合并为一个条件表达式。
做法:
- 使用适当的逻辑运算符,将条件表达式合并为一个(顺序执行的条件表达式使用逻辑或;嵌套的 if 语句使用逻辑与)
- 对合并后的条件表达式实施提炼函数
使用逻辑或合并条件表达式
重构前:
public double disabilityAmount(anEmployee) {
if (anEmployee.seniority < 2) {
return 0;
}
if (anEmployee.monthsDisabled > 12) {
return 0;
}
if (anEmployee.isPartTime) {
return 0
}
// 计算伤残金
}
重构后:
public double disabilityAmount(anEmployee) {
if (isNotEligableForDisability()) {
return 0;
}
// 计算伤残金
}
// 没有资格获得伤残金的
private boolean isNotEligableForDisability() {
return anEmployee.seniority < 2
|| anEmployee.monthsDisabled > 12
|| anEmployee.isPartTime;
}
使用逻辑与合并条件表达式
重构前:
if (anEmployee.onVacation) {
if (anEmployee.seniority > 10) {
return 1;
}
}
return 0.5;
重构后:
if (anEmployee.onVacatopm && anEmployee.seniority > 10) {
return 1;
}
return 0.5;
以卫语句取代嵌套条件表达式
条件表达式通常有两种风格:
- 如果两条分支都是正常行为,则应该使用
if...else...
条件表达式 - 如果某个条件极其罕见,旧应该单独检查该条件,并且在条件为真时立刻从函数中返回(卫语句)
范例1
重构前:
public double getPayAmount() {
double result;
if (isDead) {
result = deadAmount();
} else {
if (isSeparated) {
result = separatedAmount();
} else {
if (isRetired) {
result = retiredAmount();
} else {
result = normalPayAmount();
}
}
}
return result;
}
重构后:
public double getPayAmount() {
if (isDead) {
return deadAmount();
}
if (isSeparated) {
return separatedAmount();
}
if (isRetired) {
return retiredAmount();
}
return normalPayAmount();
}
范例2 将条件反转
重构前:
public double adjustedCapital(anInstrument) {
double result = 0;
if (anInstrument.capitail > 0) {
if (anInstrument.interestRate > 0 && anInstrument.duration > 0) {
result = (anInstrument.income / anInstrument.duration) * anInstrument.adjustmentFactor;
}
}
return result;
}
重构后:
public double adjustedCapital(anInstrument) {
if (anInstrument.capitail <= 0) {
return 0;
}
if (anInstrument.interestRate <= 0 || anInstrument.duration <= 0) {
return 0;
}
return (anInstrument.income / anInstrument.duration) * anInstrument.adjustmentFactor;
}
public double adjustedCapital(anInstrument) {
if (anInstrument.capitail <=> 0
|| anInstrument.interestRate <>= 0
|| anInstrument.duration <= 0) {
return 0;
}
return (anInstrument.income / anInstrument.duration) * anInstrument.adjustmentFactor;
}
以多态取代条件表达式
以多态取代条件表达式的场景:
- 有多处地方出现 swtich 基于不同类型做出不同的处理
- 有一个基础的逻辑(超类),在其上又有一些变体(子类)
重构前:
// 不同鸟类的速度
switch (bird.type) {
case "EuropeanSwallow":
return 35;
case "AricanSwallow":
return 40 - 2 * bird.numberOfCoconuts;
case "NorwegianBlueParrot":
return bird.isNailed ? 0 : (10 + bird.voltage / 10);
default:
return null;
}
重构之后:
BirdFactory.createBird(birdType).getSpeed();
class BirdFactory() {
public static createBird(String birdType) {
switch (birdType) {
case "EuropeanSwallow":
return new EuropeanSwallow();
case "AricanSwallow":
return new AricanSwallow();
case "NorwegianBlueParrot":
return new NorwegianBlueParrot();
default:
return new Bird();
}
}
}
class Bird {
public double getSpeed() {
return null;
}
}
class EuropeanSwallow extends Bird {
@Override
public double getSpeed() {
return 35;
}
}
class AricanSwallow extends Bird {
@Override
public double getSpeed() {
return 40 - 2 * bird.numberOfCoconuts;
}
}
class NorwegianBlueParrot extends Bird {
@Override
public double getSpeed() {
return bird.isNailed ? 0 : (10 + bird.voltage / 10);
}
}
引入特例
当一个数据结构的使用者都在检查某个特殊的值,就可以使用特例模式。
特例模式(Special Case):创建一个特例元素,用以表达对这种特例的共用行为的处理。
通常需要特例处理的值就是 null 值,这个也是这个模式常被叫做 “Null 对象” 模式的原因。
重构前:
// 第一处地方
String customerName;
if (customer == null) {
customerName = "occupant";
} else {
customerName = customer.getName();
}
// 第二处地方
double plan;
if (customer == null) {
plan = registry.getBillingPlans().getBasic();
} else {
customer.getBillingPlan();
}
// ...
重构后:
class Customer {
// ...
}
// 引入特例
class UnknownCustomer extends Customer {
@Override
public String getName() {
return "occupant";
}
@Override
public String getPlan() {
return registry.getBillingPlans().getBasic();;
}
}
// 第一处地方
String customerName = customer.getName();
// 第二处地方
double plan = customer.getBillingPlan();
// ...
引入断言
使用断言来对明确地表达某种假设。
断言用于预防程序员的错误,对于输入外部数据都应该执行检查而不能用断言实现。
// 折扣应该不小于 0
assert this.discountRate >= 0;
if (this.discountRate > 0) {
base = base - (this.discountRate * base);
}
参阅
- 《重构:改善既有代码的设计(第2版)》