“现在开始,把代码里的 ‘else’ 丢掉!”

本文讨论了编程中过度依赖if-else的常见问题,提出优先处理边缘情况并尽早返回的策略,以降低代码复杂性,提高可维护性和减少错误风险。作者提倡简化代码结构,以应对多层嵌套带来的挑战。

33d2eb9f92973f1e116b517c1dfdc6a3.gif

【优快云 编者按】这篇文章主要探讨了在编程中如何处理“正常路径”和“边缘情况”。作者指出,大多数代码库中 80% 的代码负责处理预期的“正常路径”,而剩下的 20% 负责处理错误和异常。常见的做法是使用 if-else 语句,将正常路径放在 if 块中,而边缘情况放在 else 块中。这样的做法经常导致代码复杂性增加,特别是当出现多层嵌套时问题尤为严重。作者提出了一个解决方案,即优先处理边界情况并提前返回,以减少代码的复杂性和提高可维护性。这种方法不仅可以使代码结构更清晰,还可以降低引入错误的风险。

原文链接:https://preslav.me/2023/09/22/ditch-that-else/

本文为优快云组织编译,未经授权,禁止转载!

作者 | Preslav   译者 | 明明如月

出品 | 优快云(ID:优快云news)

我在职业生涯中见过的大多数代码都遵循帕累托法则 。在任何一个代码库中,有 80% 的代码负责处理“正常路径”(即软件预期要做的事情),而其余的 20% 则用于处理错误、异常和边界情况。

9c3aff1adfc47ce5bc2f216129ab6888.jpeg

6f9c9a3e23c1c409b0c4a2433fc7be14.png

条件判断与编程心理

当我们学习编程时,很自然地会遇到 if-else 语句(这个语句在多数流行的编程语言中都存在)。我们的大脑会立即将 if 与正常路径联系起来,而将 else 与边界情况联系起来。编程中普遍存在一种心理机制:通过在一个大的 if 代码块中包裹主要逻辑,程序员会觉得更加安心。他们会认为这样做能够防止不当输入或异常情况影响代码的主要功能。至于其他情况相对次要,放到 else 里面就好了。

d6650c97124adb9a90a1f73940d453e4.png

理想很丰满,现实很骨感

这种做法经常会导致以下这样的代码:

if (someConditionIsMet) {
    // ...
    // ...
    // ...
    // 接下来是 100 行代码
    // ...
    // ...
    // ...
    // 还有 100 行
    // ...
    // ...
    // ...
} else {
  // 现在,处理边缘情况
}


return someResult;

其中一个问题是,边界情况的处理被放在了最后,这使得在上面的代码块添加代码时,很容易忽视 else 内的逻辑。我敢说,大多数人读这段代码时甚至不会注意到 else的存在,直到一个边界情况迫使他们更深入地阅读这段代码。

2d0eb35b68041673d39c3a673d32882b.png

多层嵌套的陷阱

更糟糕的是,人们倾向于在多个层级上也运用这种模式。因此,写出来的代码更像是这样:

if (someConditionIsMet) {
    // ...
    // ...
    // ...
    // 接下来是 100 行代码
    // ...
    // ...
    // ...
    // 还有 100 行
    if (someOtherConditionIsMet) {
        // ...
        // ...
        // ...
        // 接下来是 100 行代码
        // ...
        // ...
        // ...
        if (yetAnotherConditionIsMet) {
            // ...
            // ...
            // ...
            // 接下来是 100 行代码
            // ...
            // ...
            // ...
            } else {
            // 现在,处理边缘情况
            }
        // ...
        // ...
        // ...
    } else {
        // 现在,处理边缘情况
        return someOtherResult;
    }
    // ...
    // ...
    // ...
} else {
// 现在,处理边缘情况
}


return someResult;

代码的多层嵌套是存在很大隐患,也给代码库增加了很多不必要的复杂性。人脑一次只能处理几件不同的事情,因此当面临需要深入分析多个代码层级时,很容易忘记上一层的关键逻辑,导致一些不必要的错误。这也通常是一些 Pull Request 的代码审查发现问题被打回去重新修改的一个主要原因。我们的业务流程已经足够复杂了,多层嵌套又进一步增加了其复杂度。

e119206244c0328d23c83026fd051395.png

"箭头代码"的解决方案

我并非第一个提到这种现象的人。实际上,这种深层次嵌套的代码甚至有一个名称。几十年前,Jeff Atwood 就为这种代码风格创造了 [箭头代码](https://blog.codinghorror.com/flattening-arrow-code/) 这个术语。

884a4e24e641712c2ab54e7a7ee596d7.png

幸运的是,解决方案相当简单。

39e8466a1f25e0d28faf3e797cda412c.png

抛弃 else,优先处理边缘情况

如果我们抛弃 `if-else` 块中的 `else` 并优先处理这块逻辑,情况会怎么样呢?如果我们不期望某件事经常发生,难到不是先检查它、并在发生时立即返回更好吗?

先处理小的边界情况,如果必要的话提前返回;否则,将主流程保留在函数的最外层。

if (!someConditionIsMet) {
  // 首先处理那个边缘情况
  return someResultOrNothing;
}


// 主流程可以继续,不需要额外的保护块
// ...
// ...
// ...
// 再加 100 行代码
// ...
// ...
// ...
// 还有 100 行


return someResult;

同样的思路也可以应用于处理多个边缘情况:

if (!someConditionIsMet) {
  // 首先处理那个边缘情况
  return someResultOrNothing;
}


if (!someOtherConditionIsMet) {
  // 首先处理那个边缘情况
  return someResultOrNothing;
}


if (!yetAnotherConditionIsMet) {
  // 首先处理那个边缘情况
  return someResultOrNothing;
}


// 主流程可以继续,不需要额外的保护块
// ...
// ...
// ...
// 再加 100 行代码
// ...
// ...
// ...
// 还有 100 行


return someResult;

抛弃 else 就可以减少嵌套层数,有效降低代码复杂度。能够让代码更简单,结构更清晰,更容易维护。由于边界情况在函数的顶部得到了处理,开发者在后续添加新代码时不太可能忽略这些情况,从而降低了引入错误的风险。处理边缘情况并早期返回可以减少不必要的计算,从而可能提高代码的执行效率。简化的代码结构更容易被同事理解,从而使代码审查过程更加顺畅,减少了潜在错误和不良实践的传播。

实际工作中你是否见到过多层嵌套导致代码复杂度较高难以维护的情况?你是否认可文中通过边界情况提前处理来减少 else 使用的建议?还有哪些可以有效解决多层嵌套的方法?

1c1c8bf46bdf84c1a272aa129cf2653b.gif

欢迎参与 优快云 重磅发起的《2023 AI 开发者生态调查问卷》,分享您真实的 AI 使用体验,更有精美好礼等你拿!

e96221f2f164c091c31015bcc942c336.jpeg

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值