计算机科学既是一门科学,也是一门艺术。其科学方面涵盖了从计算理论和算法研究到代码设计和程序架构的各个领域。然而,当涉及到具体实现时,正是艺术的天赋、细致的风格和技术实力的结合,将好的代码与伟大的代码区分开来。
像艺术一样,代码既是主观的,也是非主观的。编码的非主观方面包括创建良好代码必须遵循的“硬性”概念:设计模式、项目结构、常用库的使用等等。尽管这些概念为开发高质量、可维护的代码奠定了基础,但真正使代码清晰、可维护和易于理解的,同时赋予代码清晰地传达意图、功能和用法的能力,是程序员技术和工具的细微之处——对齐、命名、空格的使用、上下文的使用、语法高亮和IDE的选择。
良好代码和伟大代码之间的这种区别之所以存在,是因为每个人都基于自身良好(或不良)的习惯和偏好,对自己的特定编码风格具有亲和力。任何人都可以按照设计模式或使用某些“硬性”技术编写代码,但伟大的程序员才能以清晰、简洁和易于理解的方式填充代码的细节。这一点非常重要,因为正如每个人都可以从一件艺术品中得出独特的意义或体验一样,尽管代码的架构和设计相同,但每位开发人员或代码读者都可能根据命名和其他约定从代码中推断出不同的含义。
从另一个角度来看,编程也可以被视为一种“加密”形式。程序员以各种方式设计问题的解决方案,然后以程序及其支持文件的形式加密解决方案。几个月或几年后,当需要进行更改时,新的程序员必须解密该解决方案。这通常不是一项令人羡慕的任务,这主要归咎于项目初始“加密”期间缺乏清晰的沟通。当必要的密钥存在时,解密信息很简单,当特别注意代码本身所传达的内容时,理解旧代码也很简单。
为了解决这个问题,一些著作定义了整个编程语言的单一编码标准7,而另一些著作则默许接受命名约定,只要它们是一致的6。优美的代码通常被定义为可读、专注、可测试和优雅1。更极端的情况是发明一种完全围绕一套具体理想构建的编程语言,例如 Ruby 或 Python。Ruby 强调简洁、简单、灵活性和平衡4。《Python 之禅》5 中清楚地阐述了 Python 背后的原则,其重点在于美观、简洁、可读性和可靠性。
我们解决这个问题的方法是开发一套编码指南(在线提供3)。虽然这些指南来自教育环境,但它们的设计也对从业者有用。这些指南基于几个广泛的原则,这些原则捕捉了一些基本的沟通原则,并将编码约定的概念提升到了更高的层次。使用这些约定还将提高代码库的可持续性。本文着眼于这些基本原则。
本文未考虑的一个方面是语法高亮或 IDE 的使用。虽然两者都可能使代码更具可读性(由于语法高亮、代码折叠等)并且更易于管理(例如,快速查找或重构函数和/或变量),但我们的指南的开发目的是保持 IDE 和颜色中立。它们旨在反映在任何环境中编写代码时都重要的基本原则。此外,虽然 IDE 在某些方面可以帮助提高可读性和理解力,但这些工具中提供的功能并非标准功能(例如,考虑 Visual Studio、Eclipse 和 VIM 中提供的不同功能)。同样,语法高亮在不同环境之间差异很大,并且可以轻松更改以匹配个人偏好。以下原则的目标是为良好的编程构建一个独立于编程 IDE 的基础。
在最近的一篇 文章中,Poul-Henning Kamp2 提出了一个有趣的观点,即编程语言的许多风格都源于 ASCII 字符集和基于打字机的终端。编程语言没有利用现代设备的图形属性和选项。虽然代码必须像优秀的英语语法一样清晰地编写,但它不是英语文本。相反,它更像数学和表格。
这是一个影响深远的原则。首先,它直接关系到字体的使用。不要对程序代码使用可变宽度(比例)字体,因为代码不是文本。等宽字体(例如,Courier 和 Data Gothic)看起来很吸引人,并且可以轻松对齐代码。比例(可变宽度)字体会阻止正确对齐,更重要的是,它们“看起来不像”代码。
虽然应该继续将程序视为一系列操作或高级算法,但代码的每个部分也应被视为图表、表格或菜单的呈现。在图 1、2 和 3 中,请注意垂直对齐的使用,以显示对称性。这是一种强大的沟通方法。
如果一行长代码溢出到多行,我们建议断开并重新对齐代码。例如,不要使用
participant newEntry = new participant (id, name, address1, address2, city,
state, zip, phone, email);
而要使用
participant newEntry = new participant (id, name, address1, address2,
city, state, zip, phone, email);
或者
participant newEntry = new participant(id, name, address1, address2, city,
state, zip, phone, email);
程序员在充分了解其用途的情况下为事物创建名称,并且当人们知道名称代表什么时,通常许多名称都是有意义的。因此,程序员面临的问题是:基于概念创建名称。然而,真正的挑战恰恰相反:根据名称推断概念!这是程序读者面临的问题。
考虑简单名称sputn,取自常见的 C++ 头文件<iostream.h>。没有经验或不熟悉的程序员可能会突然受到一系列问题的困扰,例如:它是整数吗?是指针吗?是数组还是结构?是方法还是变量?sp 代表 saved pointer 吗?sput 是要执行 n 次的操作吗?你是发音为 sputn 还是 s-putn 还是 sput-n 还是 s-put-n?
我们提倡基于传统的英语用法来命名——特别是,简单、非正式、缩写的英语用法。考虑以下更具体的指南。
* 变量和类应为名词或名词短语。
* 类名就像集体名词。
* 变量名就像专有名词。
* 过程名称应为动词或动词短语。
* 用于返回值的方法应为名词或名词短语。
* 布尔值应为形容词。
* 对于复合名称,保留传统的英语语法。
* 尽量使名称可发音。
图 4 显示了此广泛原则的一些示例。
当考虑诸如
numFiles = countFiles(directory);
之类的示例时,存在一个有趣但很小的问题。countFiles是一个好名字,但它不是最佳名称,因为它是一个动词。动词应保留用于对变量有影响的过程调用。对于对变量没有副作用的函数,请使用名词或名词短语。人们通常不会说
Y = computeSine(X);或者
milesDriven = computeDistance(location1, location2);
而是说
Y = sine(X);或者
milesDriven = Distance(location1, location2);
我们建议
numFiles = fileCount(directory);
是一个小的改进。更重要的是,这加强了一般规则,即动词表示过程,而名词或形容词表示函数。
在所有其他条件相同的情况下,程序越短越好。例如,用作索引变量的局部变量可以命名为i, j, k等等。在循环的每一行上使用的数组索引不需要比i更详细地命名。使用或者indexelementNumber会通过过度的描述来模糊计算的细节。很少使用的变量可能值得一个长名称:例如,MaxPhysicalAddr。当变量名称很长时,尤其是有很多变量名称时,很快就难以看清发生了什么。变量名称通常可以通过依靠其使用的上下文来缩短——例如,变量Store在堆栈实现中,而不是.
StackStore
使用空格显示结构
虽然书面和口头交流可以达到很高的清晰度水平,但如果不伴随非语言线索和倾向的个人风格,往往会显得缺乏意义。个人的肢体语言有助于阐明口语。从类似的意义上讲,程序员依靠空格——代码中未直接说明的内容——来传达逻辑、意图和理解。
一个例子是在概念上不同的代码段之间使用空行。空行应提高可读性,因为它们分隔了逻辑上不同的代码段,从而提供了相当于章节分隔的文学效果。使用空行的适当位置包括
* 从预处理器指令切换到代码时
* 在类和结构声明周围
* 在一定长度的函数定义周围
* 在一组逻辑上连接的语句周围
* 在声明和随后的可执行语句之间=考虑图 6 中的代码清单。单个空格也应用于显示单个语句内的逻辑结构。行内策略性地使用空格可以简化人类读者的解析。至少,应在参数列表中的逗号后以及赋值运算符 “<<” 和重定向运算符 “>>".
” 和 “-另一方面,不应将空格用于一元运算符,例如一元负号 (&)、地址运算符 (*)、间接运算符 (.)、成员访问运算符 (++)、递增运算符 (--).
) 和递减运算符 (
让决策结构自行说明
图 1 中使用的 case 语句提出了一个普遍的观点:非常简单的决策语句结构可以简洁地呈现,简单地显示替代代码,并且如果可能,可以不带大括号,如图 7 中的示例所示。
简单条件互斥的情况并不少见,从而创建了一种广义的 case 语句。这可以像图 8 中那样,按照常见的做法打印为链。
当然,结构可能是真正嵌套的,那么必须使用嵌套间距或函数来指示替代方案。同样,一般的观点是让结构驱动布局,而不是编程语言的语法。
关注代码,而不是注释
讨论
虽然此处介绍的指南在教育环境中使用,但它们在工业环境中也具有价值。接受这些指南教育的学生很可能会在进入工业界时使用它们(或某些变体)。为了证明这一点,我们开发了一个示例,将这些指南应用于两种截然不同的风格。第一种是 Unix 风格。它简洁,经常使用元音删除,并且经常在操作系统代码等实际应用中找到。这并不是暗示所有或大多数系统程序员都使用这种风格,只是说这种风格并不罕见。图 10 显示了这种风格的一个小例子。
我们将第二种风格称为教科书风格,如图 11 所示。同样,这绝不是暗示所有或大多数教科书都使用这种风格,只是示例中的风格并不罕见。在这种风格中,重点是学习。这意味着经常有注释,并且代码分布良好。为了学习和理解语言的细节,这种风格可能非常出色。从实践的角度来看,或者对于任何具有一定规模的程序而言,这种风格都不太适用,因为它在使用或阅读时可能会让人感到难以承受。此外,这种风格使人难以看清整体设计,就好像人们被困在树下而看不到周围的森林。
图 12 是图 10 和 11 中函数的重新设计,使用此处讨论的指南在学术代码和实用代码之间实现平滑过渡。该图显示了两种风格的平衡,更直接地依靠代码本身来清晰地传达意图和功能。与教科书风格相比,生成的代码更短更紧凑,同时仍然清晰地传达含义、意图和功能。与 Unix 风格相比,代码略长,但含义、意图和功能比原始代码更清晰。
图 13 在另一种设置中说明了此处介绍的指南。这是一个取自复杂程序(10,000 行)的函数,该程序与电力系统可靠性和关于 PHEV(插电式混合动力汽车)的能源使用有关。该程序进行了许多与此类车辆将对当前电网产生的影响以及对发电和输电系统的影响相关的计算。该程序试图通过开发使用蒙特卡罗模拟的可靠性评估模型来评估电力系统的可靠性。
虽然之前的示例显示了此处介绍的指南的优点,但反对这些指南的一个论点是,为了保持某种编码风格的完整性而进行更改非常耗时,尤其是在使用版本控制系统时。面对时间紧迫的项目或将来很可能不会更新或维护的项目,这种努力可能不值得。典型的案例包括课堂项目、博士论文或临时应用程序。
参考文献
1. Heusser, M. 2005. 优美的代码。Dr. Dobb's (八月); http://www.ddj.com/184407802.
2. Kamp, P-H. 2010. 先生,请远离 ASR-33!, 8 (10); https://queue.org.cn/detail.cfm?id=1871406.
3. Ledgard, H. 2011. 专业编码指南。未发表的报告,托莱多大学; http://www.eng.utoledo.edu/eecs/faculty_web/hledgard/softe/upload/index.php?&direction=0&order=&directory=Reading%203%20Productivity-Management.
4. Molina, M. 2007. 是什么使代码优美。Ruby Hoedown。
5. Peters, T. 2004. Python 之禅。PEP (Python 增强提案) 20 (八月); https://pythonlang.cn/dev/peps/pep-0020/.
6. Reed, D. 2010. 有时风格确实很重要。Journal of Computing Sciences in Colleges 25(5): 180-187.
致谢
作者要感谢 David Marcus 和 Poul-Henning Kemp 在完成这项工作时提出的富有洞察力的评论,以及多年来为这些指南做出贡献的软件工程专业的学生。
[email protected]
Henry Ledgard 于 1964 年获得塔夫茨大学文学学士学位,1969 年获得麻省理工学院博士学位,并在牛津大学度过一年博士后研究员生涯。他的第一个程序是用 Fortran 在穿孔卡片上编写的。他的硕士论文是关于一个图形显示设施程序,用于使用汇合方程逼近数值数据。他的博士论文试图为编程语言的语法和翻译提供生成语法。他曾在约翰·霍普金斯大学和马萨诸塞大学阿默斯特分校任教。1977 年,他加入设计团队,创建了新的编程语言 ADA,然后开始了咨询和写作实践。1989 年,他加入托莱多大学任教。他目前的两个兴趣是创造性地帮助人们学习和简化技术界面。
Robert Green 获得了日内瓦学院计算机科学学士学位、鲍ling Green 州立大学计算机科学硕士学位,目前正在托莱多大学攻读博士学位。他在多个行业拥有多年的软件开发经验。他目前的研究兴趣之一是编写高质量、可持续的代码。
© 2011 1542-7730/11/1000 $10.00
最初发表于 Queue 第 9 卷,第 11 期—
在 数字图书馆 中评论本文