要的研发成果常常产自类比( analogy )。通过把你不太理解的东西和一些你较为理解、且十分类似的东西做比较,你可以对这些不太理解的东西产生更深刻的理解。这种使用隐喻的方法叫做“建模( modeling )”。
科学史中到处都可以看到借助隐喻的力量而产生的新发现。化学家凯库勒曾梦见一条蛇咬着自己的尾巴,醒来后他意识到类似的环状分子结构正好能够解释苯的各种特性。后来的进一步实验证实了他的这一假说( Barbour 1966 )。
气体的分子运动理论则是基于一种所谓的“撞球( billiard-ball )”模型,它把气体分子想象成有质量且彼此之间发生弹性碰撞的小球,就像撞球一样。有很多有用的理论就是基于这个模型提出来的。
而光的波动理论则主要是在研究光和声音之间相似性的基础上发展起来的。光和声音都有振幅(亮度、响度)、频率(颜色、音调)和其他一些共有属性。对声波理论和光波理论进行对比研究的成果丰富,科学家们甚至付诸大量的努力,想寻找一种能在真空中传播光波的介质(就像声波能在空气介质中传播一样),并将这种介质命名为“以太( ether )”——但他们从未能找到过这种介质。虽然类比催生了丰盛的成果,这一次它却把人们引入了歧途。
不过总的来说,模型的威力就在于其生动性,让你能够把握整个概念。它能隐隐地暗示各种属性( properties )、关系( relationships )以及需要补充查证的部分( additional areas of inquiry )。不过有时候,当隐喻的概念被过度引申时,模型也会误导人们。当科学家们寻求“以太”的时候,他们就是过度地引申了模型。
正如你所预期的那样,有些隐喻比其他一些更贴切。一个好的隐喻应该是简单的,它与另一些相关的隐喻联系密切,且能够解释大部分实验证据及其他已观测到的现象。
来考虑一下这个例子:把一块沉重的石头绑在细绳上让它来回摆动。在伽利略之前,信奉亚里士多德学说的人们看到这个现象时,想到的是重物体自然地从高处坠落,落向低处并静止下来。他们会想,下落的石头遇到了阻碍。而伽利略在看到这个现象的时候却想到了钟摆( pendulum )。他认为,那块石头实际上是在不断地重复着几乎完全相同的运动。
这两种模型的启发能力是完全不一样的。亚里士多德学派的人将来回摆动的石头看作是正在下落的物体,因此会去观察石头的重量、石头被拉起的高度,以及它到达静止状态时所需要的时间。而在伽利略的钟摆模型中的要素就完全不同了。伽利略观注石头的重量、钟摆的半径、角位移以及每次摆动所花的时间。伽利略之所以能够发现亚里士多德学派的人所不能发现的单摆定律,正是因为他们所用的模型不同,这使得他们看到了不同的现象,提出了不同的问题。
隐喻在帮助人们更好地理解软件开发问题方面所做的贡献,与它帮助人们更好地理解科学问题所做的贡献一样大。在 1973 年图灵奖的演讲中, Charles Bachman 讲到了由盛行的地心说到日心说的转变。托勒密的地心说模型持续了 1400 年而没有受到严重挑战,直到 1543 年哥白尼提出了以太阳为中心的理论,这个理论认为宇宙的中心是太阳而不是地球。这一个认知模型的改变最终帮助人们发现了新的行星,并将月亮重新界定为地球的卫星而不是一颗独立的行星,它也使人们对人类在宇宙中的地位有了一个完全不同的理解。

Bachman 曾经把天文学中托勒密到哥白尼的转变,与 20 世纪 70 年代早期计算机编程方面的变化做了比较。当 1973 Bachman 做这个比较时,数据处理正在从“以计算机为中心( computer-centered )”的观点向“以数据库为中心( database-centered )”的观点转变。 Bachman 指出,过去的数据处理是把所有数据看作流经计算机( flowing through a computer )的连续卡片流( stream of cards )(以计算机为中心的观点),现在则转变为把焦点放到数据池( pool of data )上,而计算机偶尔涉足其中(以数据库为中心的观点)。
今天,我们已经很难想象还有谁会认为太阳是在绕着地球旋转的。类似地,我们也很难想象程序员还会认为所有的数据应被看作是一个连续卡片流。在这两个例子里,旧的理论被抛弃后,我们都觉得难以置信——居然还有人曾经相信过这些理论?更有意思的是,当人们正在相信旧理论时,也同样会认为新理论是那么地荒谬,正如今天我们对旧理论的看法一样。
在更好的理论出现之前,天文学家因为墨守地心说而屡屡受阻。在计算机世界里面也有类似的情况,以计算机为中心的观点也让坚持它的计算机科学家步履蹒跚,直到以数据库为中心的理论出现。
人们常常轻视隐喻的力量。对前面的每一个例子而言,很自然地有人会说:“嗯,恰当的隐喻当然是更有用,但其他隐喻都是错的!”虽然这是一种很自然的反应,实际远非如此简单。科学发展的历史并不是一系列从“错误”的隐喻到“正确”的隐喻的转变,而是一系列从“不太合适”的隐喻到“更好”的隐喻的转变,也是从不是很贴切的隐喻到更贴切的隐喻的转变,还是从在一 个方面暗示人们 到在另一个方面暗示人们的转变。
事实上,那些被更好的新模型所替代的旧模型也依然是很有用的。工程师们依旧在使用牛顿力学来解决大部分的工程问题——虽然从理论上说,牛顿力学已经被爱因斯坦的理论所取代。
相对于其他学科而言,软件开发还是一门很年轻的学科,它还没有成熟到拥有一套标准隐喻的程度。因此必然存在许多或相互补充、或相互抵触的隐喻。某些隐喻相对好一些,而另一些则比较糟糕。你对隐喻有多理解,也就决定了你对软件开发有多理解。 
与其说一个软件隐喻像是一张路线图,还不如说它是一盏探照灯。它不会告诉你到哪里去寻找答案,而仅是告诉你该如何去寻找答案。隐喻的作用更像启示( heuristic ,启发、试探法),而不是算法( algorithm )。
算法是一套定义明确的指令,使你能完成某个特定的任务。算法是可预测的( predictable )、确定性的( deterministic )、不易变化的( not subject to chance )。一个告诉你如何从 A 点到达 B 点的算法,不会让你绕路,不会让你额外地经过 D E F 等地方,更不会让你停下来闻闻玫瑰花或喝杯咖啡。
而启发式方法(试探法)是一种帮你寻求答案的技术,但它给出的答案是具有偶然性的( subject to chance ),因为启发式方法仅仅告诉你该如何去找,而没有告诉你要找什么。它并不告诉你该如何直接从 A 点到达 B 点,它甚至可能连 A 点和 B 点在哪里都不知道。实际上,启发式方法是穿着小丑儿外套的算法:它的结果不太好预测,也更有趣,但不会给你什么 30 天无效退款的保证。
驾驶汽车到达某人的家,写成算法是这样的:沿 167 号高速公路往南行至 Puyallup ;从 South Hill Mall 出口出来后往山上开 4.5 英里 ;在一个杂物店旁边的红绿灯路口右转,接着在第一个路口左转;从左边褐色大房子的车道进去,就是 North Cedar 714 号。
用启发式方法来描述则可能是这样:找出上一次我们寄给你的信,照着信上面的寄出地址开车到这个镇;到了之后你问一下我们的房子在哪里。这里每个人都认识我们——肯定有人会很愿意帮助你的;如果你找不到人,那就找个公共电话亭给我们打电话,我们会出来接你。
算法和启发式方法之间的差别很微妙,两个术语的意思也有一些重叠。就本书的目的而言,它们之间的差别就在于其距离最终解决办法的间接程度:算法直接给你解决问题的指导,而启发式方法则告诉你该如何发现这些指导信息,或者至少到哪里去寻找它们。
如果有一些能明确指导你该如何解决编程问题的信息,编程当然会更容易,结果也更易预见。但编程这门学科还没那么先进,或许永远也不可能那么先进。对于编程来说,最大的挑战还是将问题概念化( conceptualizing ),编程中的很多错误都是概念性的错误。正因为每一个问题在概念上都是独特的,所以要找到一套能解决所有问题的一通百通的指导规则是很难的、甚至是不太可能的。如此看来,能一般性地知道大致如何解决问题,至少也和知道如何解决特定问题一样有价值了。
那么该如何使用软件中的隐喻呢?应该用它来提高你对编程问题和编程过程的洞察力;用它来帮助你思考编程过程中的活动,想象出更好的做事情的方法。你不可能看到一行代码并说它违反了本章所描述的某个隐喻。但随着时间的流逝,人们会发现,相对于不善运用隐喻的人来说,那些使用隐喻来照亮自己的软件开发过程的人,他对于编程的理解会更好,并且能够更快地写出更好的代码。