
2.2#2:不必要的嵌套代码
软件的思维模型是对系统行为的内部表达方式。在编程时,我们需要维护思维模型(例如,关于整体代码交互和函数实现)。基于命名、一致性、格式等多种标准,代码被认为是可读的。可读的代码需要较少的认知努力即可维持思维模型;因此,它更易于阅读和维护。
影响可读性的一个关键方面是嵌套层的数量。让我们做一个练习。假设我们正在进行一个新项目,需要了解以下join函数的作用:

这个join函数连接两个字符串,如果长度大于 max,则返回一个子字符串。同时,它处理s1和s2上的检查,以及连接调用是否返回错误。
从实现的角度来看,这个函数是正确的。然而,建立一个包含所有不同情况的思维模型可能不是一项简单的任务。为什么?因为嵌套层的数量。
现在,让我们用相同的函数再次尝试这个练习,但实现方式不同:


你可能会注意到,尽管做的工作和以前一样,但构建这个新版本的思维模型需要更少的认知负荷。这里我们只维护两个嵌套的级别。正如 Go Time 播客的一个小组成员Mat Ryer提到的(参见链接7):
把正确的代码路径向左对齐;你应该能够快速地向下扫描一列,以查看预期的执行流。
由于嵌套的if/else 语句,在第一个版本中很难区分预期的执行流。而第二个版本向下扫描一列即可查看预期的执行流,向下扫描第二列可查看边缘情况是如何处理的,如图2.1所示。

图2.1 要理解预期的执行流,只需查看正确路径的列
一般来说,函数所需的嵌套级别越多,对其阅读和理解就越难。让我们看看这个规则在优化代码可读性方面的一些不同应用。
■ 当 if 块返回时,在所有情况下都应该忽略 else 块。例如,我们不应该写:

我们应该像下面这样省略 else 块:

在这个新版本中,先前位于 else 块中的代码被移动到顶层,便于阅读。
■ 也可以在异常代码路径的情况下遵循这个逻辑:

这个新版本更容易阅读,因为它保留了左侧边缘的正常路径,并减少了块的数量。
编写可读的代码对每个开发人员来说都是一个重要的挑战。努力减少嵌套块的数量,对齐左侧的正常代码路径,以及尽早地返回,这些都是提高代码可读性的具体方法。
在下一节中,我们将讨论 Go 项目中常见的误用:滥用init 函数。