《Unix 编程艺术》 第六章 透明性
美在计算科学中的地位,要比在其它任何技术中的地位都重要,因为软件是太复杂了。美是抵御复杂的终极武器。 - David Gelernter
如果没有阴暗的角落和隐藏的深度,软件系统就是透明的。
透明性是一种 被动品质。如果实际上能预测到程序行为的全部或大部分情况,并能建立简单的心理模型,这个程序就是 透明 的。
如果软件系统所包含的功能是为了帮助人们对软件建立正确的 做什么、怎样做
的心理模型而设计,这个软件系统就是 可显 的。 对程序员而言,良好的变量和函数名有助于提高可显性。可显性是一种主动品质。
优雅的代码事半功倍:优雅的代码不仅正确,而且显然正确:优雅的代码不仅将算法传达给计算机,同时也把见解和信心传递给阅读代码的人。通过追求代码的优雅,我们能够编写更好的代码。学习编写透明的代码是学习如何编写优雅代码的第一关,很难的一关。
#6.1 研究实例
不要让调试工具仅仅成为一种事后追加或者用过就束之高阁的东西。它们是通往代码的窗口:不要只在墙上凿出粗糙的洞,要修整这些洞并装上窗。如果打算让代码一直可被维护,就始终必须让光照进去。
#6.2 为透明性和可显性而设计
要为透明性和可显性而设计,就必须运用各种计策来保持代码的简洁,也必须专注代码同其他人交流的方式。在“这个设计能行吗?”之后要提出的头几个问题就是“别人能读懂这个设计吗?这个设计优雅吗?”
优雅不是一种奢侈,在人类对软件的反映中,这些品质对于减少软件 bug 和提高软件长期维护性是最基本的。
#6.2.1 透明性之禅
要追求代码的透明,最有效的方法很简单,就是不要在具体操作的代码上叠放太多的抽象层。
软件设计者都是聪明人,可对其处理的应用域形成概念。他们把围绕这些概念编写的软件组织起来。然后,调试时,他们经常发现很难透过这些概念弄明白到底发生了什么。
#6.2.2 为透明性和可显性而编码
透明性和可显性同模块性一样,主要是设计的特性而不是代码的特性。以下这些问题需要好好关心:
- 程序调用层次中最大的静态深度是多少?如果大于 4,就要当心
- 代码是否具有强大、明显的不变性质?(不变性质是指一个软件设计中各个操作都保持不变特性)
- 每个 API 中的各个函数调用是否正交?或者是否存在太多的
magic flags
和模式位,使得一个调用要完成多个任务?
完成避免模式标志会导致混乱的 API,里面包括太多几乎一模一样的函数,但是频繁使用模式标志更容易产生错误(很多易忘并且易混的模式标记)。
- 程序的数据结构或分类和它们所代表的外部实体之间,是否存在清晰的一对一映射?
- 是否容易找到给定函数的代码部分?不仅单个函数、模块,还有整个代码,需要花多少精力才能读懂?
- 代码增加了特殊情况还是避免了特殊情况?每一个特殊情况可能对任何其它特殊情况产生影响:所有隐含的冲突都是 bug 滋生的温床。然而更重要的是,特殊情况使得代码更难理解。
- 代码中有多少个 magic number(意义含糊的常量)?
#6.2.3 透明性和避免过度保护
隐藏细节和无法访问细节有着重要区别。
不能展示其行为的程序使故障检测困难得多。所以,经验丰富的 Unix 用户实际上把调试和探测开关的存在视为良好程序的标志,不存在则认为程序可能有问题。
#6.2.5 透明性、故障诊断和故障恢复
透明性还有一个与简化调试有关的好处,就是 bug 发作时,透明的系统更容易实施恢复措施。而且,经常是,首先更能抵抗 bug 的破坏。
简洁加上透明,降低了费用,缓解了每个人的压力,让人们从中解放出来,更多地投入到新问题,而不是总为旧错误擦屁股。
#6.3 为可维护性而设计
如果作者以外的其他人能够顺利地理解和修改软件,则这个软件就是可维护的。
Unix 程序员学到了一种品性,就是宁愿抛弃、重建代码也不愿修补那些蹩脚的代码。
一个非常重要地实践就是应用清晰原则:选择简单地算法。另一个重要的实践是要包含开发者手册。在发布源码的同时包含指导文档,简略地描述代码地关键数据结构和算法。