过度使用 If 语句的问题

过度使用 If 语句的问题

许多开发者容易过度使用 if/else,并不是因为其他解决方案复杂难懂,而是因为它们紧贴自然语言的表达方式
author
Wonderhows March 25, 2024

多少算太多?

有些人认为这个数字是 一,你应该总是至少用一个三元运算符来替代任何单一的 if 语句。我不采取那么极端的方法,但我想突出一些方法来逃离常见的 if/else 斯巴达代码。

我相信很多开发者这么容易掉入 if/else 陷阱,并不是因为其他解决方案的复杂性,而是因为它遵循了一种非常自然的语言模式: if 某事做这个,else 做那个。

等等,什么是三元运算符?

如果你已经知道了,请跳过这部分。

三元运算符与 if/else 没有革命性的差别,它们都是条件操作,但三元运算符确实返回一个值,所以它可以直接用于赋值。

const greaterThanFive = 8 > 5 ? "yep" : "nope";

console.log(greaterThanFive); // 'yep'

基本模式就是一个条件,一个如果为真返回的值,和一个如果为假返回的值。

(condition) ? isTruthy : isFalsy

If/Else 的替代方案

让我们开始一个场景并通过不同的解决方案的例子来走一遍。

我们将从用户输入中取颜色,并需要将它们转换为一些预设的颜色代码以匹配,这样我们就可以改变我们的背景颜色。所以我们将检查颜色名称的字符串并设置我们的颜色代码,如果我们有匹配。

const setBackgroundColor = (colorName) => {
  let colorCode = "";
  if (colorName === "blue") {
    colorCode = "#2196F3";
  } else if (colorName === "green") {
    colorCode = "#4CAF50";
  } else if (colorName === "orange") {
    colorCode = "#FF9800";
  } else if (colorName === "pink") {
    colorCode = "#E91E63";
  } else {
    colorCode = "#F44336";
  }
  document.body.style.backgroundColor = colorCode;
};

这个 if/else 完成了任务。但我们被大量重复的逻辑所拖累,比较 colorName 和重复分配 colorCode。

Switch

现在我们可以更恰当地将这个转换成一个 switch 语句。它更符合我们想要做的概念;我们有几个字符串案例我们想匹配,如果没有我们的案例匹配,则默认。

const setBackgroundColor = (colorName) => {
  let colorCode = "";
  switch (colorName) {
    case "blue":
      colorCode = "#2196F3";
      break;
    case "green":
      colorCode = "#4CAF50";
      break;
    case "orange":
      colorCode = "#FF9800";
      break;
    case "pink":
      colorCode = "#E91E63";
      break;
    default:
      colorCode = "#f44336";
  }
  document.body.style.backgroundColor = colorCode;
};

但是 switch 仍然带来了我们可以没有的大量样板和重复代码。

查找表

那么我们真正想要实现什么呢?我们需要将颜色名称的十六进制颜色代码分配,所以让我们创建一个对象,该对象将颜色名称作为键,颜色代码作为值。然后我们可以通过使用 object[key] 来查找我们的颜色代码。我们需要一个默认值,所以一个简短的三元运算符,如果没有找到键则返回默认值,这样做的同时使默认部分成为我们对象的一部分。

const colorCodes = {
  blue: "#2196F3",
  green: "#4CAF50",
  orange: "#FF9800",
  pink: "#E91E63",
  default: "#F44336",
};

const setBackgroundColor = (colorName) => {
  document.body.style.backgroundColor = colorCodes[colorName]
    ? colorCodes[colorName]
    : colorCodes["default"];
};

现在我们有了一个查找表,它整齐地列出了我们的输入和可能的输出。

这不是关于奇迹般的“代码行数”(LOC)减少(我们从 15 减到 20 再减到 12。)事实上,这些解决方案可能会增加你的 LOC,但我们增加了可维护性、可读性,并实际上通过只有一个逻辑检查作为默认回退减少了复杂性。

用数据交换逻辑

使用查找表而不是 if/else 或 switch 的最重要成就是,我们将多个比较逻辑实例转换为数据。代码更具表现力;它将逻辑显示为一个操作。代码更可测试;逻辑已经减少。而且我们的比较更易于维护;它们作为纯数据被整合了。

让我们将五个比较逻辑操作减少到一个,并将我们的值转换为数据。

场景:我们需要将成绩百分比转换为它们的字母成绩等价物。

一个 if/else 足够简单;我们从上到下检查成绩是否高于或等于匹配字母成绩所需的。

const getLetterGrade = (gradeAsPercent) => {
  if ((gradeAsPercent = 90)) {
    return "A";
  } else if (gradeAsPercent >= 80) {
    return "B";
  } else if (gradeAsPercent >= 70) {
    return "C";
  } else if (gradeAsPercent >= 60) {
    return "D";
  } else {
    return "F";
  }
};

但我们一次又一次地重复同样的逻辑操作。

所以让我们把我们的数据提取到一个数组中(以保持顺序)并将每个成绩可能性表示为一个对象。现在我们只需要对我们的对象做一个 >= 比较并找到我们数组中的第一个匹配项。

const gradeChart = [
  { minpercent: 90, letter: "A" },
  { minpercent: 80, letter: "B" },
  { minpercent: 70, letter: "C" },
  { minpercent: 60, letter: "D" },
  { minpercent: 0, letter: "F" },
];

const getLetterGrade = (gradeAsPercent) => {
  const grade = gradeChart.find((grade) => gradeAsPercent >= grade.minpercent);
  return grade.letter;
};

开始将你的比较想象为数据

当你需要比较或“检查”值时,自然会想到使用 if/else,这样你就可以用言语走过问题。但下一次尝试想象你的值如何可以被表示为数据,你的逻辑减少到解释那些数据。

你的代码将最终变得更可读、更易于维护,并且在其意图中更加有目的性,清晰地分离了它所代表的概念。这里所有的代码示例都是可行的,但正确的方法可以将 It Works™ 代码转变为“很棒的工作”代码。

反思

此篇文章讨论了过度使用 if/else 语句的问题,并提出了几种替代方案来优化代码结构和可维护性。在直译中,大部分内容已经相对准确地转换为中文,但仍有几处需要注意:

  • 一些表达方式并不完全贴合中文习惯,例如“斯巴达代码”(spaghetti code)直译可能不易于理解,需要找到更合适的表达。
  • 部分专业术语的翻译需要保留原文,以避免混淆,如“lookup table”直译为“查找表”,但为了清晰应再次确认其原英文术语。
  • 示例代码和一些专有名词(如颜色名称、HTML 属性)保留了原文,这是符合规则的。
  • 对于“ternary”(三元运算符)的介绍部分,可能需要更简洁的解释,以适应非专业读者的理解水平。
comments powered by Disqus