历史搜索

这些技术让你摆脱 if

游客2024-08-12 08:53:01
目录文章目录
  1. 意大利面条代码
  2. if…if 类型
  3. else if…else if 类型
  4. 重构策略
  5. 查找表
  6. 责任链模式
  7. 结语

前端日常开发中,经常会遇到一些特别繁琐冗余业务代码,特别是在维护旧项目时遇到复杂的业务逻辑嵌套在深层的 if-else 语句中就更多了。如果你是简单地增量修改不仅不会改变它的复杂性,反而会增加复杂性和降低可读性。接下来在这里给大家分享三种简单而常见的重构方法。

意大利面条代码

“意大利面条代码”指的是在处理复杂业务过程时很常见的一种现象,它通常具有以下特点:

  • 内容冗长
  • 结构混乱
  • 嵌套深

我们知道,主流编程语言都有函数或方法来组织代码。而对于意大利面条代码,我们可以将其视为满足这些特点的函数。根据语言语义的不同,可以将其分为两种基本类型:

if…if 类型

这种代码结构看起来像这样:

function demo(a, b, c) {
  if (f(a, b, c)) {
    if (g(a, b, c)) {
      // ...
    }
    // ...
    if (h(a, b, c)) {
      // ...
    }
  }

  if (j(a, b, c)) {
    // ...
  }

  if (k(a, b, c)) {
    // ...
  }
}

流程图如下:

这些技术让你摆脱 if 1

在代码实现中,我们可以通过责任链数组定义等价于 else if 的规则。

const rules = [
  {
    match: function (a, b, c) { /* ... */ },
    action: function (a, b, c) { /* ... */ }
  },
  {
    match: function (a, b, c) { /* ... */ },
    action: function (a, b, c) { /* ... */ }
  },
  {
    match: function (a, b, c) { /* ... */ },
    action: function (a, b, c) { /* ... */ }
  }
  // ...
]

rules 中的每项都具有 match 和 action 属性。此时我们可以将原来的 else if 函数重写为遍历责任链数组:

function demo (a, b, c) {
  for (let i = 0; i < rules.length; i++) {
    if (rules[i].match(a, b, c)) {
      return rules[i].action(a, b, c)
    }
  }
}

当每个责任被匹配时,原函数将直接返回,这也完全符合 else if 的语义。这样,我们实现了将复杂的 else if 逻辑拆分为单独的部分。

结语

面条代码往往出现在无脑的“粗暴、快速、猛烈”风格的开发中。许多 bug 修复是通过粗暴地在这里添加一个 if 并在多处返回语句来完成的,再加上缺乏注释,这很容易导致代码可读性降低和复杂性增加。

然而,解决这个问题其实并不复杂。这些示例之所以简单,基本上是因为强大的高级编程