〓● 如果代码可读性不佳、不容易理解,可能造成如下问题:
〓❏ 其他工程师浪费时间解读它;
〓❏ 误解导致引入缺陷;
〓❏ 其他工程师修改时破坏代码。
〓● 提高代码可读性,有时候可能使其变得更为冗长、占用更多的代码行数。这往往是有价值的权衡。
〓● 提高代码可读性往往需要同理心——想象其他人可能觉得困惑的情况。
〓● 现实生活中的场景各不相同,通常有各自面临的挑战。编写易于理解的代码几乎总是需要应用常识和判断力。
编写易于理解的代码
实现代码可读性最为常见、有效的技术打下坚实的基础。但需要牢记的是,现实生活中的每个场景都不相同,有各自的考虑因素,因此常识的运用和良好的判断力都是必不可少的。
1 使用描述性名称
名称是唯一标识事物所必需的,它们也往往能提供对事物概念的简单总结。“烤箱”这个词是某个厨房用具的唯一标识,但也明显地暗示了这件用具的用途:烧烤食物。如果我们坚持用“对象A”来指代一台烤箱,就很容易忘记“对象A”是什么东西,起什么作用。
在代码中命名不同事物也适用相同的原则。名称是唯一标识类、函数和变量等对象所必需的。但我们对事物的命名也提供了很好的机会,可以通过确保以不言自明的方式指代事物,使代码更易于理解。
2 适当使用注释
代码中的注释或文档可以起到多种作用,如:
〓● 解释代码完成的是什么;
〓● 解释代码为什么完成这些工作;
〓● 提供其他信息,如使用指南。
本节将集中说明前两个作用:使用注释解释“什么”和“为什么”。使用说明等其他信息通常组成代码契约的一部分,这些已在第3章讨论过。
概述大块代码(如一个类)作用的高层注释常常很有用。然而,在较低层次的代码细节上,解释代码作用的注释往往不是提高代码可读性的最有效手段。
在代码行级别的作用上,使用描述性名称、编写质量良好的代码应该是不言自明的。如果我们需要为代码添加许多底层注释以解释它的作用,那么这很可能是代码可读性不理想的迹象。相反,解释代码为什么存在或提供更多背景信息的注释往往相当有用,因为只用代码不总是能够说明这些。
3 不要执着于代码行数
一般来说,代码库中的代码行数越少越好。代码通常需要一定的持续维护,代码行数越多,有时就意味着代码过于复杂,或者没有重用现有的解决方案。较多的代码还会增加工程师的认知负荷,因为阅读量显然更大了。
工程师有时会在这方面走极端,认为最大限度地减少代码行数比代码质量的其他因素更重要。他们有时会抱怨,所谓的“代码质量改善”将3行代码变成10行代码,因此产生的代码更差了。
但应该牢记的是,代码行数只是我们真正关心的事情的一个替代指标,与大部分替代指标一样,这是个有用的指导原则,但并非铁律。我们真正关心的是确保代码:
〓● 容易理解;
〓● 不容易受到误解;
〓● 不容易在无意中遭到破坏。
并不是所有代码都是一样的:与10行(甚至20行)易于理解的代码相比,1行极其难以理解的代码更容易降低代码质量。5.3.1节和5.3.2节用例子阐述了这一点。
4 坚持一致的编程风格
在我们造句的时候,必须遵循某些规则,才能写出语法正确的句子。此外,我们应该遵循一些其他风格上的指南,以确保我们的句子容易理解。
举个例子,想象一下我们要写段关于“软件即服务”(Software as a service)的文字。通常,如果一个首字母缩略词包含“a”或“as”等单词,那么这些单词应该用小写字母形式。因此,人们最熟悉的“软件即服务”缩写为SaaS。如果我们将这个缩略词写成SAAS,阅读文档的人可能会误以为我们指的是其他事物,因为那不是他们预期中的“软件即服务”的缩写。
这也同样适用于代码。语言的语法和编译器规定了允许的写法(有点像语法规则),但在工程师编写代码时,我们对于采用的风格惯例有很大的自由度。
5 避免深嵌套代码
典型的代码由相互嵌套的块组成,如:
〓● 函数定义一个调用时运行的代码块;
〓● if语句定义条件为真时运行的代码块;
〓● for循环定义每次迭代时运行的代码块。
图6 使函数调用易于理解
如果函数命名得当,它的作用应该是显而易见的,但即便有了这样的函数,如果参数的目的和作用不明,函数调用也很可能无法理解。1说明了控制流逻辑(如if语句和for循环)造成代码块相互嵌套的情况。代码中的指定逻辑通常有不止一种构造方法。有些形式可能造成许多代码块嵌套,而其他方法可能几乎不会造成任何嵌套。考虑代码结构对可读性的影响是很重要的。
1 控制流逻辑(如if语句和for循环)可能造成代码块相互嵌套
6 使函数调用易于理解
如果函数命名得当,它的作用应该是显而易见的,但即便有了这样的函数,如果参数的目的和作用不明,函数调用也很可能无法理解。
7 避免使用未做解释的值
在许多情况下我们需要硬编程的值。常见例子如下:
〓● 数值转换中的系数;
〓● 可调整的参数,例如某项任务失败时的最大重试次数;
〓● 代表某些值填写模板的字符串。
所有硬编程值都有两个重要的信息:
〓● 具体值——计算机在执行代码时必须知道这一信息;
〓● 值的意义——工程师只有知道了这一信息,才能理解代码。没有这一信息,工程师将无法理解代码。
代码需要一个显而易见的值,否则代码很可能无法编译或起作用,但工程师在编写该值的时候很容易忘记向其他工程师澄清该值的实际含义。
8 正确使用匿名函数
匿名函数是没有名称的函数。它们通常在需要的时候于代码中内嵌定义。不同编程语言定义匿名函数的语法也各不相同。
9 正确使用新奇的编程语言特性
每个人都喜欢新鲜的事物,工程师也不例外。许多编程语言仍在积极发展,而编程语言设计师也在不断增加令人喜爱的新特性。当这种情况发生时,工程师往往渴望利用新特性。
编程语言设计师在增加新特性之前经过了非常谨慎的思考,因此在许多情况下,新特性都可能使代码更加易于理解或者鲁棒。工程师因为这些新特性而兴奋是很好的事情,因为这增大了利用新特性改善代码的可能性。但如果你发现自己渴望使用新奇的编程语言特性,一定要保持坦诚的态度,思考这一特性是不是适合手头使用的工具。
推荐书籍
- 《代码整洁之道》(Clean Code):这本书强调了编写干净、可维护代码的重要性,并提供了一些实用的编程技巧和原则。
- 《代码整洁之道:程序员的职业素养》(Clean Coder: A Code of Conduct for Professional Programmers):这本书不仅关注于编写干净的代码,还强调了程序员应该遵循的一些职业素养和道德准则。
- 《好代码 ,坏代码》:本书教你如何像高效的软件工程师一样思考代码,如何编写读起来像一个结构良好的句子的函数,如何确保代码可靠且无错误;如何进行有效的单元测试,如何识别可能导致问题的代码并对其进行改进,如何编写可重用并适应新需求的代码,如何提高读者的中长期生产力;同时还介绍了如何节省开发人员及团队的宝贵时间,等等。