学写提高性能的代码之流程控制-2

228 阅读5分钟

这是我参与8月更文挑战的第8天,活动详情查看: 8月更文挑战

前言

业务代码开发多了,其实就是在写 if-else

上篇文章从性能优化方面阐述了if-else和switch的使用时机,本文接着讲流程控制中利用索引查询或对象的属性查询达到类似条件判断的目的。

上文连接:学写提高性能的代码之流程控制-1


数组索引和对象属性

除了if-else语句和switch语句,利用数组的索引查询或对象的属性查询也可以达到类似条件判断的目的,代码如下:

// 条件映射数组
const valueArray = [value0, value1, value2, value3, value4]
// 提取对应数组索引的处理
valueArray[value]

由于数组中的每个元素既可以是对象,又可以是函数,那么便可将条件匹配的处理过程封装到数组的元素中,并用数组索引映射对应的value变量,通过匹配数组索引执行相应的处理过程。同样基于对象属性的映射方式,也能实现类似的条件查找行为,代码如下:

// 基于对象的属性映射
const valueMap = {
  'condition0': () => { // 处理过程 },
  'condition1': () => { // 处理过程 },
  'condition2': () => { // 处理过程 },
}
// 提取对应对象属性的处理
valueMap[value]

基于对象属性的索引可以不局限于整数取值,它能匹配符合对象属性名命名规范的任何字符串形式,与switch方式也类似,其取值范围是离散值。当匹配条件的数量较小时,并不适合使用这种基于数组或对象的查找方式,因为查找数组或映射对象属性值往往比执行少量的条件判断语句要慢,只有当取值范围变得非常大时,这种查找方式的性能优势才会凸显出来。
另外,这种基于对象属性直视的方式,就是应用了设计模式中的策略模式。

策略模式

策略模式就是定义一系列的处理流程或算法,把它们分别封装起来,使得它们可以相互替代.其目的就是将算法的使用和实现分离。一个基于策略模式的程序通常会包含两个部分,一部分是一组策略类,其中包含一系列具体的处理算法,有点类似于包含不同处理过程的映射对象value Map;另一部分是环境类,它将根据上下文操作运算,决定调用策略类中的哪个策略处理过程,即完成流程控制中条件匹配的部分。
策略模式很好地利用了组合、委托及多态等技术思想,有效地避免了类似if-else等多种条件选择语句。同时它完美支持了开放一封闭原则,将处理算法封装在独立的策略中,使其易于理解、切换和扩展。
接下来看一个具体的场景:假设年底公司要根据员工的绩效等级发奖金,绩效考核打S的发四倍月工资,打A的发三倍月工资,打B的发两倍月工资……为简化说明,这里仅以三种绩效考核等级为例。若以通常的解决方案,直接的想法是定义一个函数,接受两个参数,分别是员工的月薪和绩效考核等级,然后在其中通过i-else判断来分别计算出奖金额度,简单实现如下:

// 计算奖金的方法
function calculateBonus(salary, level){
  if(level === 'S'){
    return salary * 4
  } else if(level === 'A') {
    return salary * 3
  } else if(level === 'B') {
    return salary * 2
  }
}

从上述代码可以看出这样的实现本身并没有什么问题,将所有奖金计算规则都包含在一个函数中一起解决。如果奖金规则计算变得复杂起来,比如老板觉得衡量指标不能只看绩效,还要看出勤、团队、组内互评等,那么对于上述计算奖金的函数来讲是非常复杂的。即便完成后,若隔年换一个开发人员来添加老板的新规则,会发现之前的这个函数不符合“对扩展开放,对修改封闭”的设计原则,扩展新功能的工作量不亚于推倒重做。若重构为策略模式的写法,代码示例如下:

// 奖金发放策略类
const strategies = {
  'S': (salary) => salary * 4,
  'A': (salary) => salary * 3,
  'B': (salary) => salary * 2,
}
// 计算具体奖金
function calculateBonus(salary, level){
  return strategies[level](salary)
}

如此解耦了各种级别奖金计算的逻辑,如果要针对不同级别奖金发放算法进行调整,则只需要修改策略类中对应的方法即可。

条件判断的使用建议

关于使用条件判断时,代码的书写建议如下。

  • 当所要匹配的条件仅为一两个离散值时,或者容易划分不同取值范围时,使用if-else语句。
  • 当所要匹配的条件超过一两个但少于十个离散值时,使用switch语句。
  • 当所要匹配的条件超过十个离散值时,使用基于数组索引或对象属性的查找方式。