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

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

  • 分解条件表达式:处理复杂的条件表达式
  • 合并条件表达式:厘清逻辑组合
  • 以卫语句取代嵌套条件表达式:在主要处理逻辑之前先做检查
  • 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 amount
      result = 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 amount
  return 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;
  }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~卷心菜~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值