代码重构之消除分支结构

3

一. 为什么要消除 if-else

当写下第一行 if-else 时,就为发展成几十个 if-else 埋下了伏笔.

当一个 if-else 有可能臃肿时,那么它一定会臃肿.  --墨菲

一个例子:

$userType = '';
if ($userTypeID == 1){
    $userType = 'Admin';
}elseif($userTypeID == 2){
    $userType = 'Corporate Customers';
}

看起来非常美妙,简洁.但是当系统慢慢复杂化,角色不断的丰富,就变成这样的代码:

$userType = '';
if ($userTypeID == 1){
    $userType = 'Admin';
}elseif($userTypeID == 2){
    $userType = 'Corporate Customers';
}elseif($userTypeID == 3){
    $userType = 'Editorial Users';
}elseif($userTypeID == 4){
    $userType = 'Photographers';
}elseif($userTypeID == 5){
    $userType = 'Vendors';
}

来个极端的例子:

图片描述

可以说非常可怕了.

二. 如何消除 if-else

消除这种条件里都是定值的 if-else 我们可以采用 查表法 来消除.

通过容器将对应的关系进行存储。通过运算得出相应关系中的其中一方的结果,再通过这个结果去容器中找对应的另一个内容

通俗的说,就是建立一个对象或者数组的 hash 表, key 值存储条件判断, value 存储符合该条件判断的逻辑处理.
比如上面的代码可以写成:

  const USER_TYPE_ADMIN = 1;
  const USER_TYPE_CORPORATE = 2;
  const USER_TYPE_EDITORIAL = 3;
  const USER_TYPE_PHOTOGRAPHERS = 4;
  const USER_TYPE_VENDORS = 5;

  public function getUserTypeName($key = null){
    $data = array(
      self::USER_TYPE_ADMIN => 'Admin',
      self::USER_TYPE_CORPORATE => 'Corporate Customers',
      self::USER_TYPE_EDITORIAL => 'Editorial Users',
      self::USER_TYPE_PHOTOGRAPHERS => 'Photographers',
      self::USER_TYPE_VENDORS => 'Vendors',
    );
    return $key === null ? $data : (isset($data[$key]) ? $data[$key] : '');
  }

这样,上面的一大段的 if-else 就一行代码搞定: $userType = User::getUserTypeName($userTypeID)

用 JavaScrip 语言来描述:

let userTypeObj = {
    1: 'Admin',
    2: 'Corporate Customers',
    3: 'Editorial Users',
    4: 'Photographers',
    5: 'Vendors',
};

let userTypeName = userTypeObj[userTypeID];

上面的例子只是不同的 userTypeID 获取不同的 userTypeName. 考虑更复杂的情况,当不同的 userTypeID 时进行不同逻辑处理.

if (userTypeID == 1) {
    //Todo ...
} else if(userTypeID == 2) {
    //Todo ...
} else if(userTypeID == 3) {
    //Todo ...
} else if(userTypeID == 4) {
    //Todo ...
}

这个时候可以把里面的逻辑处理代码抽出来为一个函数:

let userTypeObj = {
    1: () => {
        //Todo...
    },
    2: () => {
        //Todo...
    },
    3: () => {
        //Todo...
    },
    4: () => {
        //Todo...
    },
};

userTypeObj[userTypeID]();

以上代码也可以以 php 的可变变量/面向对象的多态特性来实现.

缺点:查表法的实现,要做越界检查,还要确保下标计算不会重复,如果使用面向对象的多态特性来实现,也有策略类增多等问题.

这个查表法的实现,其实是策略模式的思想.

策略模式指的是定义一系列的算法,把他们一个个的封装起来.策略模式的目的就是将算法的使用与算法的实现分离开来.

更多关于策略模式的讲解,请看这里

三. 总结

if-else 当然是有用的,比如 mysql 链接, curl 的返回码等等.当开始写的时候,只有一个 if-else ,我们可以不去消除,但是当要写到第三个第四个分支,或者每个条件分支里面都有大段大段的逻辑处理代码,我们就要考虑消除它了.

当条件语句不是定值,而是范围值的时候,如何消除 if-else 求教:

if(x < 30){
    //todo
}else if(x >= 30 ){
    //todo
}

如果觉得我的文章对你有用,请随意赞赏

你可能感兴趣的

载入中...