本文由Austin发表
指导原则
我们要谈论在一个编程语言中的最佳实践,那么我们首先应该明确什么是“最佳”。如果您们听了我昨天那场讲演的话,您一定看到了来自 Go 团队的 Russ Cox 讲的一句话:
软件工程,是您在编程过程中增加了工期或者开发人员之后发生的那些事。 — Russ Cox
Russ 是在阐述软件“编程”和软件“工程”之间的区别,前者是您写的程序,而后者是一个让更多的人长期使用的产品。软件工程师会来来去去地更换,团队也会成长或者萎缩,需求也会发生变化,新的特性也会增加,bug 也会被修复,这就是软件“工程”的本质。
我可能是现场最早的 Go 语言用户,但与其说我的主张来自我的资历,不如说我今天讲的是真实来自于 Go 语言本身的指导原则,那就是:
- 简单性
- 可读性
- 生产率
您可能已经注意到,我并没有提性能或者并发性。实际上有不少的语言执行效率比 Go 还要高,但它们一定没有 Go 这么简单。有些语言也以并发性为最高目标,但它们的可读性和生产率都不好。 性能和并发性都很重要,但它们不如简单性、可读性和生产率那么重要。
简单性
为什么我们要力求简单,为什么简单对 Go 语言编程如此重要?
我们有太多的时候感叹“这段代码我看不懂”,是吧?我们害怕修改一丁点代码,生怕这一点修改就导致其他您不懂的部分出问题,而您又没办法修复它。
这就是复杂性。复杂性把可读的程序变得不可读,复杂性终结了很多软件项目。
简单性是 Go 的最高目标。无论我们写什么程序,我们都应该能一致认为它应当简单。
可读性
Readability is essential for maintainability. — Mark Reinhold, JVM language summit 2018 可读性对于可维护性至关重要。
为什么 Go 代码的可读性如此重要?为什么我们应该力求可读性?
Programs must be written for people to read, and only incidentally for machines to execute. — Hal Abelson and Gerald Sussman, Structure and Interpretation of Computer Programs 程序应该是写来被人阅读的,而只是顺带可以被机器执行。
可阅读性对所有的程序——不仅仅是 Go 程序,都是如此之重要,是因为程序是人写的并且给其他人阅读的,事实上被机器所执行只是其次。
代码被阅读的次数,远远大于被编写的次数。一段小的代码,在它的整个生命周期,可能被阅读成百上千次。
The most important skill for a programmer is the ability to effectively communicate ideas. — Gastón Jorquera ^1 程序员最重要的技能是有效沟通想法的能力。
可读性是弄清楚一个程序是在做什么事的关键。如果您都不知道这个程序在做什么,您如何去维护这个程序?如果一个软件不可用被维护,那就可能被重写,并且这也可能是您公司最后一次在 GO 上面投入了。
如果您仅仅是为自己个人写一个程序,可能这个程序是一次性的,或者使用这个程序的人也只有您一个,那您想怎样写就怎样写。但如果是多人合作贡献的程序,或者因为它解决人们的需求、满足某些特性、运行它的环境会变化,而在一个很长的时间内被很多人使用,那么程序的可维护性则必须成为目标。
编写可维护的程序的第一步,那就是确保代码是可读的。
生产率
Design is the art of arranging code to work today, and be changeable forever. — Sandi Metz 设计是一门艺术,要求编写的代码当前可用,并且以后仍能被改动。
我想重点阐述的最后一个基本原则是生产率。开发者的生产率是一个复杂的话题,但归结起来就是:为了有效的工作,您因为一些工具、外部代码库而浪费了多少时间。Go 程序员应该感受得到,他们在工作中可以从很多东西中受益了。(Austin Luo:言下之意是,Go 的工具集和基础库完备,很多东西触手可得。)
有一个笑话是说,Go 是在 C++ 程序编译过程中被设计出来的。快速的编译是 Go 语言用以吸引新开发者的关键特性。编译速度仍然是一个不变的战场,很公平地说,其他语言需要几分钟才能编译,而 Go 只需要几秒即可完成。这有助于 Go 开发者拥有动态语言开发者一样的高效,但却不会面临那些动态语言本身可靠性的问题。
Go 开发者意识到代码是写来被阅读的,并且把阅读放在编写之上。Go 致力于从工具集、习惯等方面强制要求代码必须编写为一种特定样式,这消除了学习项目特定术语的障碍,同时也可以仅仅从“看起来”不正确即可帮助开发者发现潜在的错误。
Go 开发者不会整日去调试那些莫名其妙的编译错误。他们也不会整日浪费时间在复杂的构建脚本或将代码部署到生产中这事上。更重要的是他们不会花时间在尝试搞懂同事们写的代码是什么意思这事上。
当 Go 语言团队在谈论一个语言必须扩展时,他们谈论的就是生产率。
标识符
我们要讨论的第一个议题是标识符。标识符是一个名称的描述词,这个名称可以是一个变量的名称、一个函数的名称、一个方法的名称、一个类型的名称或者一个包的名称等等。
Poor naming is symptomatic of poor design. — Dave Cheney 拙劣的名称是拙劣的设计的表征。
鉴于 Go 的语法限制,我们为程序中的事物选择的名称对我们程序的可读性产生了过大的影响。良好的可读性是评判代码质量的关键,因此选择好名称对于 Go 代码的可读性至关重要。
选择清晰的名称,而不是简洁的名称
Obvious code is important. What you can do in one line you should do in three. — Ukiah Smith 代码要明确这很重要,您在一行中能做的事,应该拆到三行里做。
Go 不是专注于将代码精巧优化为一行的那种语言,Go 也不是致力于将代码精炼到最小行数的语言。我们并不追求源码在磁盘上占用的空间更少,也不关心录入代码需要多长时间。
Good naming is like a good joke. If you have to explain it, it’s not funny. — Dave Cheney 好的名称就如同一个好的笑话,如果您需要去解释它,那它就不搞笑了。
这个清晰度的关键就是我们为 Go 程序选择的标识符。让我们来看看一个好的名称应当具备什么吧:
- 好的名称是简洁的。一个好的名称未必是尽可能短的,但它肯定不会浪费任何无关的东西在上面,好名字具有高信噪比。
- 好的名称是描述性的。一个好的名称应该描述一个变量或常量的使用,而非其内容。一个好的命名应该描述函数的结果或一个方法的行为,而不是这个函数或方法本身的操作。一个好的名称应该描述一个包的目的,而不是包的内容。名称描述的东西越准确,名称越好。
- 好的名称是可预测的。您应该能够从名称中推断出它的使用方式,这是选择描述性名称带来的作用,同时也遵循了传统。Go 开发者在谈论惯用语时,即是说的这个。
接下来让我们深入地讨论一下。
标识符长度
有时候人们批评 Go 风格推荐短变量名。正如 Rob Pike 所说,“Go 开发者想要的是合适长度的标识符”。^1
Andrew Gerrand 建议通过使用更