if-else重构优化

1,144 阅读5分钟

引言:

在我们编程写代码的时候,我们会用到if-else或者switch-case的条件判断来负责绝大部分代码,但是不合理的使用if-else条件判断会使我们的程序复杂度大大提升,同时也会使代码的可读性急速下降,后期维护难度也大大提高。现在我们一起来用几种方法重构优化一下。

正文:

A、合理拆解

我们在符合条件下要执行函数时,当这段函数代码片段相当长,或者该函数是一段大型函数时,在带有复杂逻辑的条件判断中,检查条件的代码和实现功能的代码会让我们不清楚是要实现什么功能,从而使代码的可读性大大降低。我们来看一段代码片段:

toothAppointCancel(res: any) {
  if (TYPE === XXXX || TYPE === YYYY) {
      history.replace({
      pathname: `/appointmentdetail?jdAppointmentId=${res.msg}&type=${TYPE}`,
    });
    } else {
    window.location.reload();
  }
}

该代码片段主要实现了如果当前场景符合某些业务线时去做相应的功能实现,然而从代码表现上看,我们比较难以很快的看出代码所表达的意图。我们可以将他拆解为多个独立的函数,拆解的函数可以根据功能来命名,并在原修改处调用该独立的函数来清楚表达我们的意图。

image

让我们实现一下吧:

toothAppointCancel(res: any) {
  const isTooth = () => {
    return TYPE === XXXX || TYPE === YYYY;
  };
  const handleToothUpdate = () => {
    history.replace({
    pathname: `/appointmentdetail?jdAppointmentId=${res.msg}&type=${TYPE}`,
  });
  const handleReload = () => {
    window.location.reload();
  };
  isTooth() ? handleToothUpdate() : handleReload();
}

B、合并逻辑

有这么一种情况,我们有很多的条件检查,但是执行的功能函数却是一致的,那么我们可以用”逻辑与“或者”逻辑或“来把他们合并成一个表达式。如果我们这些彼此独立的条件判断可以被视为同一次检查的场景时,一次检查的意图明显在可读性上优于多次的条件检查。我们来看一段代码片段:

if (!(staffInfo.patientName && staffInfo.phone)) {
  // doSomething
}
...
if (!(staffInfo.phone && staffInfo.idCardNum)) {
  // doSomething
}

image

我们可以修改为如下:

if(!(staffInfo.patientName && staffInfo.phone) || !(staffInfo.phone && staffInfo.idCardNum)){
  // doSomething
} 

C、提前退出

当我们在一个if条件判断里面再次编写一个if判断,并且里面的if条件依赖于外部的if条件时,我们也可以用”逻辑与“来优化一下这个实现。我们来看一下这个代码片段:

function handleEvent(event) {
  if (event) {
    const target = event.target;
    if (target) {
      ...
    }
  }
}

我们检查了下对象event是否为真,以及属性target是否可用。我们可以如下修改:

image

function handleEvent(event) {
  if (!event || !event.target) {
    return;
  }
  ...
}

D、return调整逻辑优先级

如果我们的if-lese嵌套层级过多并且当我们判断只有一个条件分支是正常的,另一个是异常情况,如果异常不容易出现时应该单独检查这个异常的条件。我们可以区分每一层条件的优先级,把优先级高的放在第一层进行判断,优先级低的放在后面判断,这样我们可以比较容易的分辨出每一层条件的重要性,可以突出逻辑重点。”每一个函数都应该只有一个入口和一个出口“观念,我们也可以用在if-else中用return模拟这种思想。

image

我们看一个代码片段:

if (!res) {
  if (relativesType === 1) {
    // doSomething
  }else {
    if (formData.sex === 2 && formData.married === 2) {
    // doSomething
  }
}

优化后:


function errorMery(){Toast.show('婚姻状态与套餐不符');}
function alreadyMery(){setDialogInfo({
  ...dialogInfo,
  btnType: 1,
  titleText: '温馨提示',
  contText: MARRIED_TEXT,
  isDialogShow: true,
})}
if (!res) return;
if (relativesType === 1) return errorMery();
if (currLabel === 'married') return alreadyMery();

E、用多态替换switch场景

我们在写switch的时候,对于新手来说忘记写break实在是再普通不过了,就算是老猿忘记写也是时有发生的事情,而这个语法错误在诸多的语法检查器上没有办法检查出来的,因为从语法角度来说是正确的,可是代码的处理逻辑却是错误的。Switch尽管对于break很宽容,但是对判断条件很严苛,case后面只能设置常量。还有一种情况是有一个基础逻辑,基于这个基础逻辑又有一些变体,那么我们可以把基础逻辑放到超类,把各种变体逻辑放到各个子类。基于此,我们可以用面向对象中多态的特性来优雅的改善这些问题。 我们来看一段代码片段:

fixAppointToUrl: (obj: any, type: number) => {
  let url = '';
  switch (type) {
    case TYPEA:
      ...
      url = "B_JD_PE_FIX_URL";
      break;
    case TYPEB:
      ...
      url = "BC_TOOTH_FIX_URL";
      break;
    case TYPEC:
      ...
      url = "C_BACTERIN_FIX_URL";
      break;
    case TYPED:
      ...
      url = "B_TOOTH_FIX_URL";
      break;
    default:
      ...
      url = "DEFAULT_FIX_URL";
  }
  return url;
}
repeatAppointToUrl: (obj: any, type: number) => {
   let url = '';
   switch (type) {
   case TYPEA:
      ...
      url = "B_JD_PE_REPEAT_URL";
      break;
   case TYPEB:
      ...
      url = "BC_TOOTH_REPEAT_URL";
      break;
   case TYPEC:
      ...
      url = "C_BACTERIN_REPEAT_URL";
      break;
   case TYPED:
      ...
      url = "B_TOOTH_REPEAT_URL";
      break;
   default:
      ...
      url = "DEFAULT_REPEAT_URL";
   }
   return url; 
}

我们可以创建一个工厂函数返回一个对象实例,然后将条件逻辑函数转移到超类,在子类中创建函数复写超类中条件表达式的函数。

image

优化后:

class HandleUrl {
  get fixUrl(){
    return 'DEFAULT_FIX_URL';
  }
  get repeatUrl(){
    return 'DEFAULT_REPEAT_URL';
  }
}
class TYPEA extends HandleUrl{
   get fixUrl(){
    return 'B_JD_PE_FIX_URL';
   }
   get repeatUrl(){
    return 'B_JD_PE_REPEAT_URL';
   }
}
class TYPEB extends HandleUrl{
   get fixUrl(){
    return 'BC_TOOTH_FIX_URL';
   }
   get repeatUrl(){
    return 'BC_TOOTH_REPEAT_URL';
   }
}
class TYPEC extends HandleUrl{
   get fixUrl(){
    return 'C_BACTERIN_FIX_URL';
   }
   get repeatUrl(){
    return 'C_BACTERIN_REPEAT_URL';
   }
}
class TYPED extends HandleUrl{
   get fixUrl(){
    return 'B_TOOTH_FIX_URL';
   }
   get repeatUrl(){
    return 'B_TOOTH_REPEAT_URL';
   }
}

我们使用时可以创建一个子类对象调用该业务线特定的url连接:

const typed = new TYPED();
console.log(typed.fixUrl)

总结:

对于改善优化if-else条件逻辑代码,我们使用了分解条件来处理复杂的条件表达式,可以使用合并条件来清晰的梳理逻辑,可以使用return改善嵌套条件逻辑,可以使用多态优化switch判断,由此我们可以在具体使用传统条件逻辑判断的场景之外多了一种功能实现的选择。