第 17 章 一致性
一致性是降低系统复杂性、使其行为更容易理解的有力工具。系统保持一致,意味着相似的事情采用相似的做法,不同的事情采用不同的做法。一致性让已有知识可以复用:理解了一处的做法,就能立即理解其他采用相同做法的地方,不必从头学习。如果系统的实现缺乏一致性,开发人员就必须逐一了解每种情况,花费更多时间。
本章概览一致性为什么重要,怎么保持?
一致性还能减少失误。系统缺乏一致性时,两种实际不同的情况可能看起来一样。开发人员看到熟悉的模式,可能会根据过去的经验作出错误假设。反之,如果系统保持一致,根据熟悉情形作出的假设就有可靠依据。因此,一致性能让开发人员工作得更快,失误更少。
17.1 一致性示例
一致性适用于系统的许多层级,下面列举一些例子。
名称。 第 14 章已经讨论了以一致的方式使用名称的好处。
编码风格。 如今,开发组织通常会制定编码风格指南,在编译器强制执行的规则之外,进一步约束程序的结构。现代风格指南涵盖缩进、花括号位置、声明顺序、命名、注释等方面,也会限制使用被认为有风险的语言特性。风格指南让代码更易读,还能减少某些类型的错误。
接口。 同一接口的多种实现也是一致性的例子。了解其中一种实现后,其他实现就更容易理解,因为你已经知道它们应提供哪些特性。
设计模式。 设计模式是针对常见问题的公认解决方案,例如用于用户界面设计的模型-视图-控制器(MVC)方法。如果现有设计模式适合你的问题,采用它通常能更快完成实现,也更有可能奏效,读者也更容易理解代码。第 19.5 节将详细讨论设计模式。
不变量。 不变量是变量或结构始终满足的性质。例如,存储文本行的数据结构可能会强制要求每行始终以换行符终止。不变量减少了代码中必须考虑的特殊情况,并且更容易推断代码的行为。
17.2 确保一致性
保持一致性并不容易,尤其是许多人长期参与同一项目时。一个小组可能不了解另一个小组的约定;新成员也可能因为不了解约定而无意中违反,甚至建立与之冲突的新约定。下面介绍一些建立和保持一致性的技巧:
文档。 用文档列出最重要的全局约定,例如编写编码风格指南。把文档放在开发人员容易看到的地方,例如项目维基(Wiki)的显著位置。鼓励新成员阅读,也鼓励已有成员不时回顾。网上有不同组织公开的风格指南,可以选一份作为起点。
对于局部约定,例如不变量,应在代码中找到合适的位置记录。约定不写下来,其他人就不太可能遵循。
强制执行。 即使文档写得好,开发人员也很难记住所有约定。确保约定得到执行的最佳方法,是编写工具自动检查违规情况,并要求代码通过检查后才能提交到仓库。自动检查对低层级的语法约定尤其有效。
我最近的一个项目遇到了行终止符问题。一些开发人员使用 Unix,以换行符结束一行;另一些使用 Windows,通常以回车符加换行符结束一行。在一个系统上编辑另一个系统处理过的文件时,编辑器有时会把所有行终止符改成当前系统的格式。这样,文件看起来每一行都发生了变更,真正有意义的修改反而难以辨认。我们约定只使用换行符作为行终止符,但很难确保每个开发人员的所有工具都遵守。每次有新成员加入,都会出现大量行终止符问题,直到他们适应约定。
最后,我们编写了一个简短脚本,在向源代码仓库提交变更之前自动运行。脚本检查所有修改过的文件,只要有一行包含回车符,就中止提交。它也支持手动运行,将回车符加换行符替换为换行符,修正格式不符合约定的文件。这个脚本一下就解决了问题,也帮助新开发人员熟悉了约定。
译者图解把换行约定变成提交门槛
代码审查也是执行约定、帮助新开发人员掌握约定的机会。审查者要求越严格,团队成员就越快学会这些约定,代码也越整洁。
入乡随俗。 最重要的一条约定,是每个开发人员都应遵循这句古老格言:“到了罗马,就按罗马人那样做。”在新的文件中编写代码时,先看看现有代码如何组织:公有变量和方法是否都声明在私有变量和方法之前?方法是否按字母顺序排列?变量名采用 firstServerName 这样的驼峰命名法,还是 first_server_name 这样的蛇形命名法?凡是看起来像约定的做法,都应遵循。作设计决策时,也要想想项目其他地方是否可能作过类似决策;如果有,就找到现有示例,在新代码中采用相同做法。
不要改变现有约定。 克制“改进”现有约定的冲动。有一个“更好的想法”并不足以成为引入不一致的理由。 新想法也许确实更好,但保持一致带来的收益,几乎总是大于不同做法之间的优劣差距。引入不一致之前,先问两个问题:你现在是否掌握了制定旧约定时尚无法获得的重要新信息,足以支持采用新做法?新做法是否好到值得花时间更新所有旧用法?如果你的组织认可这两个问题的答案都是“是”,就进行更新,并在完成后彻底消除旧约定的痕迹。不过,其他开发人员仍可能因为不了解新约定,在今后重新引入旧做法。总的来说,重新讨论已经确立的约定,通常不值得开发人员花时间。
17.3 做过头了
一致性不仅要求相似的事情采用相似做法,也要求不同的事情采用不同做法。如果过分追求一致,强行对不同事物采用相同方法,例如给确实不同的事物使用相同变量名,或把现有设计模式套在不适合的任务上,就只会增加复杂性和混乱。只有当开发人员确信“如果它看起来像 x,它确实是 x”时,一致性才有益。
译者图解外观一致,还要含义一致
17.4 结论
一致性也体现了投资思维。保持一致需要额外投入:确定约定、编写自动检查工具、查找可供新代码参考的类似情况,以及在代码审查中帮助团队成员掌握约定。回报是代码更容易理解,开发人员能更快、更准确地看懂代码的行为,从而提高工作效率,减少缺陷。