4.2.2 正交性
正交性是有助于使复杂设计也能紧凑的最重要特性之一。在纯粹的正交设计中,任何操作均无副作用;每一个动作(无论是API调用、宏调用还是语言运算)只改变一件事,不会影响其它。无论你控制的是什么系统,改变每个属性的方法有且只有一个。
显示器就是正交控制的。你可以独立改变亮度而不影响对比度,而色彩平衡控制(如果有的话)也独立于前两个属性。想象一下,如果按亮度按钮会影响色彩平衡,这样的显示器调节起来会有多么困难:每次调节亮度之后还得调节色平衡进行补偿。更糟糕的是,如果对比度控制也影响色平衡,那么要改变对比度或色平衡同时保持另一个不变,你必须严格按照正确的方法同时调节两个旋钮。
非正交的软件设计不胜枚举。例如,代码中常见的一类设计错误出现在从某一(源)格式到另一(目标)格式进行数据读取和解析过程中。如果设计者想当然地认为源格式总是存储在某个磁盘文件中,那么他可能会编写一个打开和读取指定文件名的转换函数。但是,通常情况下,输入也完全有可能就是一个文件句柄。如果转换函数是正交设计的,例如,无需额外打开一个文件,那么以后当转换函数要处理来自标准输入、网络套接字或其它来源的数据流时,可能会省事一些。
人们通常认为Doug McIlroy“只做好一件事”的忠告是针对简单性的建议。但是,这句话也暗含了对正交性至少同等程度的强调。
如果一个程序做好一件事之外,顺带还做其它事情的时候既不增加系统的复杂度也不会使系统更易产生bug,就没什么问题。我们将在第9章检视一个名为ascii的程序,这个程序能打印ASCII字符的同名符,包括十六进制值、八进制值和二进制值;其副作用是可以对0-255范围内的数字进行快速进制转换。这第二个作用并不算违反正交性,因为所有支持该用途的特性全部是主功能所必需的,而且这样也没有增加程序文档化或维护的难度。
如果副作用扰乱了程序员或用户的思维模式,带来种种不便甚至可怕的结果(最好还是忘掉吧),这就是出现了非正交性问题。尤其在没有忘记这些副作用时,你总要被迫做额外工作来抑制或修正它们。
《程序员修炼之道》(The Pragmatic Programmer)[Hunt-Thomas]一书中对正交性以及如何达到正交性有精彩的讨论。正如该书所指出的,正交性缩短了测试和开发的时间,因为那种既不产生副作用也不依赖其它代码副作用的代码校验起来要容易得多——需要测试的情况组合要少得多。如果正交性代码出现问题,把它替换掉而不影响系统其余部分也很容易做到。最后,正交性代码更容易文档化和复用。
重构(refactoring)概念是作为“极限编程(Extreme Programming)”学派的一个明确思想首次出现的,跟正交性紧密相关。重构代码就是改变代码的结构和组织,而不改变其外在行为。当然,自从软件领域诞生之日起,软件工程师就一直在从事这项工作,给这种做法命名并把重构的一套技术方法确定下来,则非常有效地帮助了人们集中思路。因为重构概念与 Unix设计传统关注的核心问题非常契合,所以Unix开发者很快就吸收了这一术语和它的思想[3]。
Unix的基本API设计在正交性方面虽不完美,但也颇为成功。比如,我们理所当然地认为能够打开文件进行写入操作,而无需为此进行排他锁定。并不是所有的操作系统都如此优雅。老式(System III)的信号就不是正交的,因为信号接收的副作用是把信号处理器(signahandler)重置成缺省的“接收即崩溃”(die-on-receipt)[S2] 。许多大幅修正也不是正交的,如BSD套接字API,还有一些更大的修正也不是正交的,如X window系统的绘图库。
但是,就整体而言,Unix API是一个很好的例子:否则,将不仅不会、也不可能这么广泛地被其它操作系统上的C库效仿。所以,即便不是Unix程序员,Unix API也值得学习,因为从中可以学到一些关于正交性的东西。
| 回书目 上一节 下一节 |


























