Skip to content

多分支代码处理

LYF edited this page May 20, 2020 · 6 revisions

先来看这段代码

if (status === OrderStatus.waiting) {
   // 继续查询
}

if (status === OrderStatus.cancel) {
   // 取消退出
   commit('setModal', false);
   return
}

if (status === OrderStatus.deductFail) {
   commit('setModal', false);
   my.alert({
      title: alertTitle,
      content: getMessage(message) || queryOrderFailTip,
   })
   return
}

if (status === OrderStatus.pay) {
   commit('setModal', false);
   // 跳转
   my.redirectTo({ url })
   return
}

if (status === OrderStatus.rejoin) {
   // 继续查询
}

if (status === OrderStatus.fail) {
   commit('setModal', false);
   my.alert({
      title: alertTitle,
      content: getMessage(message) || queryOrderFailTip,
   })
   return
}

if (status === OrderStatus.success) {
   commit('setModal', false);
   // 跳转
   my.redirectTo({ url })
   return
}

有个同学在群里发起了投票,这段代码好还是不好,投票结果:

13人参与,其中12人认为代码不好(92%),1人认为代码好(8%)

我们先来看下,通常这种代码可以怎么改写,可能第一个想到的就是switch case

方案一 switch case

const { WAITING, REJOIN, DEDUCT_FAIL, FAIL, PAY } = OrderStatusEnum;

switch(status) {
 // 继续查询
 case WAITING:
 case REJOIN:
   break;
 case DEDUCT_FAIL:
 case FAIL:
   commit('setModal', false);
   my.alert({
     title: alertTitle,
     content: getMessage(message) || queryOrderFailTip,
   })
   return;
 case PAY:
   commit('setModal', false);
   my.rediectTo({ url })
   return;
 // more case...
}

方案二 可能有些同学还能想到map的方式

const { WAITING, REJOIN, DEDUCT_FAIL, FAIL, PAY } = OrderStatusEnum;

const mapping = {
  // 继续查询
  [WAITING]() {},
  [REJOIN]() {},
  [DEDUCT_FAIL]() {
    commit('setModal', false);
    my.alert({
      title: alertTitle,
      content: getMessage(message) || queryOrderFailTip,
    })
    return true;
  },
  [FAIL]() {
    commit('setModal', false);
    my.alert({
      title: alertTitle,
      content: getMessage(message) || queryOrderFailTip,
    })
    return true;
  },
  [PAY]() {
    commit('setModal', false);
    my.rediectTo({ url })
    return true;
  },
}

const shouldReture = mapping[status]();

if (shouldReture) { return; }

// ...

方案三 组内同学提供的另外一种方式

  1. 多分支逻辑,建议保留 do noting的代码处理,而不是保存在注释中;
  2. 合并同类的代码逻辑,进行分支收敛;
  3. 异常非主要逻辑优先处理;
  const { WAITING, REJOIN, CANCEL, DEDUCT_FAIL, FAIL, PAY } = OrderStatusEnum;

  // 继续查询
  if ([ REJOIN, WAITING ].includes(status)) {
    console.log('status is', status, 'do nothing');

    return;
  }

  commit('setModal', false);

  // 取消退出
  if (status === CANCEL) {
    console.log('status is', status, 'do nothing');

    return;
  }
  
  // 代扣失败
  if ([ FAIL, DEDUCT_FAIL ].includes(status)) {
    console.error('failed status:', status);

    my.alert({
      title: alertTitle,
      content: getMessage(message) || queryOrderFailTip,
    })

    return;
  }

  if ([ PAY, SUCCESS ].includes(status)) {
    console.log('status is', status, 'jump out', url);

    // 跳转
    my.redirectTo({
      url
    })

    return;
  }

  console.warn('Unknown status', status);

大家的一些讨(争)论(执)

  1. 老代码的问题

    • 有空if
    • 有重复代码
  2. switch case的问题

    • fail throght问题,如果忘写break或者return,那么不知道是不是故意让fail throght
    • 不能写复合条件
  3. map的问题

    • 需要在函数中return出去特定的值,来规定外部的行为
    • 也是无法写符合条件
  4. 是否需要保留空if

    • 有人认为不该保留,如有有状态没被使用,写在注释里即可
    • 有人认为保留是最好的,如果注释,有谁会关心?如果顺手删了怎么办?并且社区我们也常见到 if (true) { // do nothing } 的代码,而且写上空if,下次更改git diff最少
  5. 变量命名

    • 这个没啥争论的,如果是枚举值,后面要带Enum的后缀,且枚举值都要大写,中间使用_连接

最后的结论

关于多分支的代码编写,参考方案三,定为团队代码编写规范提倡的方案,供各个项目的主owner进行参考,不做强求,但是,项目owner有权要求项目的参与者按照规范强制执行

大家对分支的优化,各有各的看法,其实也不是那么好统一的,社区也有很多讨论

https://medium.com/front-end-weekly/switch-case-if-else-or-a-lookup-map-a-study-case-de1c801d944

Clone this wiki locally