编写可读代码的艺术

《编写可读代码的艺术》是一本不错的讲解如何让代码变得易于理解的书。书虽然不厚,但以具体的例子传递了非常多实用的方法和技巧。相信看完这本书,你或许能写出更易于阅读和理解的代码。以下是我的读书笔记。

代码应当易于理解

  • 可读性基本定理:代码的写法应当使别人理解它所需的时间最小化。
  • 尽管减少代码行数是一个好目标,但把理解代码所需的时间最小化是一个更好的目标。
  • 要经常地想一想其他人是不是会觉得你的代码容易理解。如果你这样,你肯定会成为一个更好的程序员,会产生更少的缺陷,从工作中获得更多的自豪。

表面层次的改进

  • 把信息装入名字中。
  • 把名字当做一条小小的注释,好的名字可以承载很多信息。
  • 选择专业的词。比如,如果是从网络获取资源,fetch 这个动词就比 get 更专业更准确。
  • 避免像 tmp, data 这样泛泛的名字。尽量找到更精确的名字。
  • 如果你养成多花几秒钟想出个好名字,你会发现你的“命名能力”很快提升。
  • 用具体的名字代替抽象的名字。
  • 为名字附带更多细节信息,比如加说明属性的前缀,或单位后缀。
  • 在小的作用域可以使用短的名字。
  • 使用首字母缩写的时候有一条原则:团队新成员能否理解这个名字的含义。
  • 可以利用名字的格式来传递含义,比如使用不同的格式来命名不同类型的变量。
  • 有目的地使用大小写、下划线等。

不会误解的名字

  • 小心可能会有歧义的名字。要多问自己几遍,这个名字会被别人解读成其他的含义吗。
  • 推荐使用 min 和 max 前缀来表示(包含)极限。
  • 推荐使用 first 和 last/begin 和 end 来表示包含/排除的范围。
  • 通常来讲,加上 is、has、can 或 should 这样的词,可以把布尔值变得更明确。
  • 与使用者的期望相匹配。有些名字之所以让人误解是因为用户对他们的含义有先入为主的印象。

审美

  • 好的源代码应当“看上去养眼”。
  • 使用一致的布局,让读者很快就习惯这种风格。
  • 如果多个代码块做相似的事情,尝试让它们有同样的剪影。
  • 把相关的代码分组,形成代码块。
  • 选一个有意义的顺序,始终一致地使用它。比如变量声明和使用顺序。
  • 把声明按块组织起来。因为我们的大脑会自然按照分组和层次结构来思考。
  • 用空行把代码分成“段落”,把相关的内容放在一个段落。这样很容易让读者知道读到哪里了。
  • 一致的风格比“正确”的风格更重要。

该写什么样的注释

  • 注释的目的是尽量帮助读者了解得和作者一样多。
  • 当你写代码时,你的脑海里会有很多有价值的信息。当其他人读你的代码时,这些信息已经丢失了。
  • 不要为那些从代码本身就能快速推断的事实写注释。
  • 不要为了注释而注释。
  • 不要给不好的名字加注释,应该把名字改好。
  • 很多好的注释仅通过“记录你的想法”就能得到,也就是那些你在写代码时有过的重要想法。
  • 你应该在代码中加入注释来记录你对代码有价值的见解。
  • 代码始终在演进,在这个过程中肯定会有瑕疵,不要不好意思把这些瑕疵记录下来。可以使用一些比如 TODO,HACK,FIXME 之类的标记。
  • 给常量加注释。提供关于这个常量背后的故事,为什么是这个值。
  • 想象你的代码对于外人来讲看起来是什么样子的。用注释回答意料之中的问题、说明可能的陷阱。
  • 在文件/类的级别上使用“全局观”注释来解释所有的部分是如何一起工作的。
  • 用注释来总结代码块,使读者不致迷失在细节中。

写出言简意赅的注释

  • 让注释保持紧凑。避免使用不明确的代码。润色粗糙的句子。
  • 精确地描述函数的行为。用精心挑选的输入/输出例子来进行说明。
  • 声明代码的高层次意图,而非明显的细节。
  • 用嵌入的注释(如function(/* arg = */...))来解释难以理解的函数参数。
  • 用信息含量高的词来使注释简洁。

把控制流变得易读

  • 把条件、循环以及其他控制流的改变做得越“自然”越好。运用一种方式使得读者不用停下来重读你的代码。
  • 条件语句中的比较,左侧应该倾向于被比较的值。右侧倾向于用来比较的常量。
  • 默认情况下都用 if/else,三目运算只在最简单的情况下使用。
  • 最小化嵌套。嵌套越多,读者需要记忆的就越多,思维负担就越大。
  • 通过提早返回可以减少嵌套。

拆分超长的表达式

  • 把你的超长表达式拆分成更容易理解的小块。
  • 拆分表达式最简单的方法就是引入一个额外的变量,让他来表示一个小一点的子表达式。你可以叫它“额外变量”,因为它可以解释子表达式的含义。
  • 要小心“智能”的小代码,它们往往在以后会让别人读起来感到困惑。
  • 对于复杂的逻辑判断,可以考虑从反方向解决问题。
  • 把一些重复的表达式提取出来作为函数开头的总结变量。这也是一个 DRY 的例子。

变量和可读性

  • 变量越多,就越难全部跟踪它们的动向。所以要避免不必要的变量。
  • 变量的作用域越大,就需要跟踪它的动向越久。所以要缩小变量的作用域。
  • 变量改变得越频繁,就越难以跟踪它的当前值。
  • 不要让你的同事在读你代码的时候感觉就像你在面试他们。

抽取不相关的子问题

  • 看看某个函数或代码块,问问自己,这段代码高层次的目标是什么?
  • 对于每一行代码,问一下,它是直接为了目标而工作吗?这段代码高层次的目标是什么?
  • 如果足够的行数在解决不相关的子问题,抽取代码到独立的函数中。
  • 抽取出不相关的子问题后,读者就可以关注于高层次的目标,不必为复杂的细节分心。
  • 拆分出小函数后,小函数本身更易于维护和改进。
  • 永远都不要安于使用不理想的接口。你总是可以创建你自己的包装函数来隐藏接口的粗陋细节。

一次只做一件事

  • 应该把代码组织得一次只做一件事情。这样读者一次只需要思考一件事情。

把想法变成代码

  • 如果你不能把一件事情解释给你祖母听的话,说明你还没有真正理解它。
  • 用自然语言清楚地描述逻辑。
  • 了解函数库是有帮助的。编写精炼代码的一部分工作是了解你的库提供了什么。
  • 把一个问题(或想法)变成语言真的可以让它更具体。

少写代码

  • 最好读的代码就是没有代码。
  • 通过重用库或者减少功能,你可以节省时间并且让你的代码库保持精简节约。
  • 如果你真的仔细检查你的需求,有时你可以把它削减成一个简单的问题。
  • 让你的代码库越小,越轻量级越好。要小心代码的“重量”。
  • 每隔一段时间,花 15 分钟来阅读标准库中的所有函数/模块/类型的名字。

深入阅读