重构手法——简化条件逻辑

程序大部分功能来自条件逻辑,复杂度也大多来自条件逻辑。我们有时候需要借助重构把条件逻辑变得更容易理解。比如:

  • 分解条件表达式:处理复杂的条件表达式
  • 合并条件表达式:厘清逻辑组合
  • 以卫语句取代嵌套条件表达式:在主要处理逻辑之前先做检查
  • switch逻辑:以多态取代条件表达式

分解条件表达式(Decompose Conditional)

本重构手法其实只是提炼函数的一个应用场景。我们可以对条件判断和每个条件分支分别运用提炼函数

// 假设计算购买某样商品的总价(总价=数量x单价),商品在冬季和夏季的单价不同:
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();function summer() {return !aData.isBefore(plan.summerStart) && !aData.isAfter(plan.summerEnd);
}
function summerCharge() {return quantity * plan.summerRate;
}
function regularCharge() {return quantity * plan.regularRate + plan.regularServiceCharge;
}

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

做法:

  • 确定这些条件表达式都没有副作用
  • 使用适当的逻辑运算符,将两个相关条件表达式合并为一个
  • 重复前面的合并过程,直到所有相关的条件表达式都合并到一起
function disabilityAmount(anEmployee) {if (anEmployee.seniority < 2) return 0;if (anEmployee.monthsDisabled > 12) return 0;if (anEmployee.isPartTime) return 0;// ...
}// 合并后提炼函数
function disabilityAmount(anEmployee) {if (isNotEligableForDisability()) return 0;// ...
}
function isNotEligableForDisability() {return ((anEmployee.seniority < 2) || (anEmployee.monthsDisabled > 12) || (anEmployee.isPartTime));
}

以卫语句取代嵌套条件表达式(Replace Nested Conditional With Guard Clauses)

条件表达式通常有两种风格:两个条件分支都属于正常行为;只有一个条件分支是正常行为,另一个分支则是异常的情况。对于第二种情况,如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回,这样的单独检查通常被称为“卫语句”。

// 范例一
function payAmount(employee) {let result;if (employee.isSeparated) {result = {amount: 0, reasonCode: 'SEP'};} else {if (employee.isRetired) {result = {amount: 0, reasonCode: 'RET'};} else {// logic to compute amountresult = someFinalComputation();}}return result;
}
// 修改完后:
function payAmount(employee) {if (employee.isSeparated) return {amount: 0, reasonCode: 'SEP'};if (employee.isRetired) return {amount: 0, reasonCode: 'RET'};// logic to compute amountreturn someFinalComputation();
}// 范例二:条件反转
function adjustedCapital(anInstrument) {let result = 0;if (anInstrument.capital > 0) {if (anInstrument.interestRate > 0 && anInstrument.duration > 0) {result = (anInstrument.income / anInstrument.duration) * anInstrument.adjustmentFactor;}}return result;
}
// 修改后:
function adjustedCapital(anInstrument) {if (   anInstrument.capital      <= 0 || anInstrument.interestRate <= 0 || anInstrument.duration     <= 0) {return 0;}return (anInstrument.income / anInstrument.duration) * anInstrument.adjustmentFactor;;
}

以多态取代条件表达式(Replace Conditional With Polymorphism)

复杂的条件逻辑是编程中最难理解的东西之一,有时候我们可以将条件逻辑拆分到不同的场景,从而拆解复杂的条件逻辑。这种拆分有时候用条件逻辑本身的结构就足以表达,但使用类和多态能把逻辑的拆分表达得更清晰。多态是面向对象编程的关键特性之一,它也很容易被滥用,并不是所有条件逻辑都应该用多态取代。

// 示例中:有一群鸟,鸟的特征为速度有多快和羽毛是什么样
function plumages(birds) {return new Map(birds.map(b => [b.name, plumage(b)]));
}
function speeds(birds) {return new Map(birds.map(b => [b.name, airSpeedVelocity(b)]));
}
function plumage(bird) {switch (bird.type) {case 'EuropeanSwallow':return 'average';case 'AfricanSwallow':return (bird.numberOfCoconuts > 2) ? 'tired' : 'average';case 'NorwegianBlueParrot':return (bird.voltage > 100) ? 'scorched' : 'beautiful';default:return 'unknown';}
}
function airSpeedVelocity(bird) {switch (bird.type) {case 'EuropeanSwallow':return 35;case 'AfricanSwallow':return 40 - 2 * bird.numberOfCoconuts;case 'NorwegianBlueParrot':return (bird.isNailed) ? 0 : bird.voltage / 10;default:return null;}
}
// 重构后...
function plumages(birds) {return new Map(birds.map(b => createBird(b)).map(bird => [bird.name, bird.plumage]));
}
function speeds(birds) {return new Map(birds.map(b => createBird(b)).map(bird => [bird.name, bird.airSpeedVelocity]));
}function createBird(bird) {switch (bird.type) {case 'EuropeanSwallow':return new EuropeanSwallow(bird);case 'AfricanSwallow':return new AfricanSwallow(bird);case 'NorwegianBlueParrot':return new NorwegianBlueParrot(bird);default:return new Bird(bird);}
}class Bird {constructor(birdObject) {Object.assign(this, birdObject);}get plumage() {return 'unknown';}get airSpeedVelocity() {return null;}
}
class EuropeanSwallow extends Bird {get plumage() {return 'average';}get airSpeedVelocity() {return 35;}
}
class AfricanSwallow extends Bird {get plumage() {return (bird.numberOfCoconuts > 2) ? 'tired' : 'average';}get airSpeedVelocity() {return 40 - 2 * bird.numberOfCoconuts;}
}
class NorwegianBlueParrot extends Bird {get plumage() {return (bird.voltage > 100) ? 'scorched' : 'beautiful';}get airSpeedVelocity() {return (bird.isNailed) ? 0 : bird.voltage / 10;}
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部