重构 - 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版)》
posted @ 2022-10-22 23:54  廖子博  阅读(83)  评论(0编辑  收藏  举报