如何提升程序的可读性?像写书那样去写程序

在以前的文章中我提过程序员核心能力这个概念

你需要找到一些核心的技术能力。它们使你更快速地工作,产生更高质量的成果,不容易被后来者超越,最最重要的一点是,不会因为某项技术的过时而失去价值。

在我看来,能写出简洁易懂的程序就是一项核心能力。为什么这么说呢?

首先,这是一项很有价值的能力。可读性好的程序更容易得到维护与复用,这就提升了你的工作价值。它还可以为你在团队中建立良好的个人声誉。而当你成为团队中的主力时,你的代码会被团队其它成员效仿与借鉴,所以它的影响力会超越自身,带动整个团队的代码质量。

其次,它是一项要经过长时间磨练才能逐渐精进的能力。虽然从表面看,一个人对一门语言越是熟悉,他写的代码的可读性就越好,但本质上这是一种逻辑表达能力,与具体的编程语言关系并不大。掌握这项能力的人,当他使用一门新的编程语言时,程序的可读性也很快可以达到相近的水准。换句话说,这项能力是不会因某项技术而过时的。

本文就和大家聊聊什么样的代码是可读性好的,该如何提升这方面的能力。

程序与写作

想要提升自己程序的可读性,就要先了解它到底指的是什么?从字面上看,它是指别人阅读你程序时的一种主观体验,有一种说法“程序可读性好坏,可以通过别人阅读代码时,骂脏话的频率来衡量”。这个解释很传神,但没什么可操作性,如何才能让别人看你代码时少说点脏话呢?

这里我给出另一种解释,可读性反映了作者将程序内在逻辑传递给读者的能力。依据这个解释,你会发现写程序与写作有很多相同之处,尤其类似于科普作品的写作。两者的目的都是要将自己的逻辑思维简洁、准确地表达出来,降低别人理解的难度。只是一个用自然语言来表达,另一个用更严谨的编程语言来表达。

不过另一方面,从读者的阅读方式上讲,两者又有一些不同。读书一般就是从头至尾的顺序阅读,但别人会如何阅读你的程序呢?这要从别人读源码的目的出发,一般可能有下面几种情况

他想要了解你程序里的某项功能是如何运作的?

他需要修复你程序中的某个 Bug

他要在你的程序上做二次开发,增添新功能或是扩展现有功能

他是以学习的心态来拜读你的代码

在你成为大师之前,我们先不考虑最后一种情况。在前3种情况里,1)与2)是比较类似的,读者的目的都是想要知道某个具体功能的代码在哪里,以及它是如何被实现的。3)则需要读者对代码库的结构有整体的了解,才能找到在现有代码库之上实现新功能最合理的做法。

整体结构的组织

如果把一个代码库比做一本书,代码库的文件结构以及顶层函数就是书的目录。每个功能模块是书的一个章节,每个章节应是一个单独的主题,并且章节与章节之间的关系应该是松散。自上而下的各级子函数相当于章节内部的大节、小节以及段落。

书是一种树型结构,上层节点是目录和各级标题,叶子结点就是正文。同样,代码库也是树形结构,上层结点是模块和一些顶层函数,叶子结点是底层函数的具体实现。代码阅读与一般书籍阅读的不同之处在于,读书是有序依次遍历每个叶子节点,读代码则往往是在整个树上不同层次的子结点间来回切换,直至找到读者最关心的那个叶子结点为止。

为了让读者能高效地阅读,我们的代码树应该具备以下特点

不能太深,也就是不应该有太多函数嵌套

不能太宽,也就是单个函数不能过长

平衡,即每个节点的子节点深度应该是均匀的。也就是函数里的每个子函数(或是语句)应该处于大体相同的逻辑层次。在一个函数里,不应该一会儿在描述高层次的逻辑,一会儿又去描述一些底层细节。

要仔细地为每个节点命名,因为好的命名能让读者快速地自上而下找到他关心的那个叶子结点。这就是为什么函数的命名很重要。

具体代码的编写

上一节讲的是代码整体结构的组织。接下来我们来看,写具体的逻辑代码时,程序与写作之间又有何相通之处?

首先,读书时大家都喜欢排版整齐的文章,这让人阅读时有一种舒适感。类似的,如果一个代码库有统一的代码风格(Coding Style),就会给读者一个良好的第一印象。代码风格中每一条都看似微不足道,例如,单行的最大字符数,缩进的空格数,长语句如何断行,数学符号与变量之间是否需要空格等等。但当所有这些元素组合在一起时,你的代码就会有一种品质感,也给读者带来更好的阅读体验。不过我也反对走另一个极端,就是一切代码都要严格符合风格规定。比如为了把单行字数控制在80个字符之内,而在一个非常奇怪的位置断行,或是把一个完整的逻辑硬生生拆成了两个语句来写。代码风格是一些指导性原则,在适当的时候可以变通,不要过于死板。

其次,在写作时,特别是科普性的文章里,我们不需要充满华丽词藻的长句,能够用简短的语句将自己的逻辑清晰地描述出来,才是高手。类似的,在写程序时,我们应力求用简洁的语句来描述程序逻辑。现代语言中有一些特性,像是 List Comprehension, Inline functions, Destructuring 等,运用这些特性可以将原本需要若干行代码完成的功能,在一行代码里写完。适当使用这些特性有助于达到代码的简洁。但若是一味地追求缩短代码行数,过度使用,反而会降低可读性。所以代码是要简单,而不是要短,要注意两者的区别。

除了上述的两点,写程序与写文章还有许多的相似之处。例如,文章的段落之间会空行,函数中的语句也可以用空行分段。合理地分段有助于别人的阅读体验。代码里的注释就如同文章里的脚注,在必要之处可以加上。但过多的注释就是作者表达能力不足的表现了。

阅读自己的代码

我在写文章时有一个习惯,就是在写作过程中,先尽量一气呵成地将自己要表达的观点写下来,暂时不去在意用词或是语句的流畅性,只把握大结构,尽量保证在这个过程中思路不被打断。当大体内容写完后再反复阅读,并在这个过程中细调用词与语句,有时也会调整段落间的前后关系,让文章从整体到细节的表达都更加顺畅。我发现这个方法对提高我写作的效率与品质都有帮助。

其实一样的方法也可以用在写代码中。当对程序的整体逻辑形成一个思路后,可以先快速地将主体逻辑转成代码写下来。这一阶段不必太过在意一些结构或是代码风格上的细节。每写完一整段逻辑后再反复阅读自己的代码,并重构不合理的语句与函数,直到对这部分代码满意为止。

编程语言的表达能力

虽然每种编程语言都能写出优雅的代码,但不能否认,编程语言的表达能力是有区别的。表达能力强的编程语言更接近自然语言,在可读性上就有先天性的优势。所以在为一个项目选择语言时,编程语言本身的表达能力应该是一个重要的指标。例如,不要用 Bash 去编写复杂的命令行脚本,换 Python 来写。需要用到 JavaScript 的项目,尽量去学习与使用 ES2017 或是 TypeScript,而不是停留在 ES5 或是更早版本的语法中。

但另一方面,编程语言并不能代替你思考,如何组织你的代码结构,梳理你的代码逻辑,仍然要靠自己去解决。就像我现在用母语在写这篇文章,但依旧不是一件轻松的事。

程序不仅仅是代码

很多时候“程序可读性”与“代码可读性”被认为是同一件事,但程序不仅仅是代码。

前文中提到,可读性的本质是作者把自己程序中的思维逻辑传递给读者的能力。虽然代码在这个传递中起了至关重要的作用,但它并不是唯一的途径。作者还可以通过文档,单元测试,日志文件等方面来让读者理解他的程序。例如,单元测试提供了函数输入与输出的范例,日志文件能更详细地打印出程序的运行轨迹与中间状态,文档对可读性的帮助更是显而易见。

优秀的程序员会利用所有这些部分,来帮助读者理解自己程序的运行机制。希望你也不要忽略它们。

小结

一个人如果对于技术没有热情,他可能不会太过关注自己程序的可读性。公司往往在这方面也很欠缺,对可读性的关注远不如对程序正确性与性能的关注。这是因为后面两者会产生更直接的短期结果,而可读性是一种长期投资。从长远来讲,一方面它是对代码维护成本的投资,另一方面它是对程序员核心素质的投资。不妨现在反思一下,你的团队在 Code Review 方面做得如何?而你自己在程序正确运行后,又会花了多少时间来提升可读性呢?

最后,对于 Pythoner,我推荐大家拜读一下Flask的源码,在可读性方面堪称典范。另外 Robert Martin 写的《代码整洁之道 Clean Code》,虽是十年前的老书,但其中的原则在今天依然适用,如果你想提升这方面的能力,绝对值得一读。

今天的分享就到这里,希望对大家有所帮助。有兴趣的同学也可以看看我之前两篇关于程序员核心能力的文章

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180201G1HRFZ00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券