计算机科学拥有在所有领域中最丰富多彩的语言,在计算机科学中,我们会听到诸如 “病毒”,“特洛伊木马”,“蠕虫”,“崩溃” 等一系列看似与计算机无直接关系的词汇。

这些词汇都是隐喻,它们描述了特定的软件现象。生动的隐喻可以帮助人们增加对软件开发过程的理解。

这里提到的 “隐喻(比喻)” 和 “类比” 在语境上极为类似,本节中可以将两者视为一物。

#隐喻的重要性

重要的发展往往时从类比中产生的。通过将不了解的主题与更了解的主题进行比较,你就可以得出一定的见解,从而更好的理解不熟悉的主题。这种隐喻被称作为 “建模”。

当我们需要在许多不同的选项中进行选择时,模型会很有帮助。它们让我们想象不同的场景,以便选出最佳的一个。
———— 《贪婪的多巴胺》 摘抄

科学史上充满了基于利用隐喻力量的发现:

  • 化学家凯库勒做了一个梦,梦中他看到一条蛇用嘴叼着尾巴。当他醒来时,他意识到基于类似环状形状的分子结构可以解释苯的性质,进一步的实验证实了这一假设。
  • 气体动力学理论基于“台球”模型。人们认为气体分子具有质量并且可以弹性碰撞,就像台球一样,并且从这个模型中得出了许多有用的定理。
  • 光的波动理论主要是通过探索光和声音之间的相似性而发展起来的。光和声音具有振幅(亮度、响度)、频率(颜色、音调)和其他共同属性。声音和光的波动理论之间的比较非常富有成效,以至于科学家们花费了大量的精力来寻找一种能够像空气传播声音一样传播光的介质。他们甚至给它起了一个名字——“以太”——但他们从未找到这种媒介。

从 “以太” 的示例可以看出,隐喻在有些时候是具有误导性的。

隐喻的价值不应该被低估。隐喻可以提供让所有人都能理解的预期行为。减少不必要的沟通和误解。让学习和教育的过程更快。实际上,隐喻是一种内化和抽象概念的方式,让人的思维处于更高的层次,避免低级的错误。
—— Fernando J. Corbató(1990 年图灵奖得主)

人们很容易轻视隐喻的力量。对于前面的例子,人们可能会本能的做出反应是:“嗯,正确的隐喻当然有用,但也有错误的隐喻。”但科学史并不是一系列从“错误”隐喻到“正确”隐喻的转换,而是从“糟糕”的隐喻到“更好”的隐喻、从包容性较小到包容性更强、从一个领域的暗示性到另一个领域的暗示性等一系列变化。

而且,很多模型在被更好的模型替代后仍然有用。比如从理论上上,牛顿力学已经被爱因斯坦理论取代,但工程师们仍然使用牛顿力学来解决大部分工程问题。

与大多数其他科学相比,软件开发是一个年轻的领域,它还不够成熟,没有一套标准的隐喻。因此,它有大量互补和冲突的隐喻。有些比其他更好。有些更糟。你对隐喻的理解程度决定了你对软件开发的理解程度。

设计模式一定程度上,也是隐喻

#如何使用软件隐喻

软件隐喻更像是探照灯而不是路线图。它不会告诉你在哪里可以找到答案,它只是告诉如何找到答案。隐喻更像是一种启发式方法,而不是一种算法。

算法是一组用于执行特定任务的明确定义的指令。算法是可预测的、确定性的,并且不受偶然性的影响。算法会告诉你如何从 A 点到 B 点:不走弯路,不绕道到 D、E 和 F 点。

启发式是一种帮助您寻找答案的技术。其结果可能会受到偶然性的影响,因为启发式方法只告诉您如何查看,而不是告诉您要查找什么。它不会告诉你如何从 A 点直接到达 B 点;它甚至可能不知道 A 点和 B 点在哪里。

以驾车前往某人家为例子:

  • 算法:沿 167 号高速公路向南行驶至皮阿拉普 (Puyallup)。从 South Hill Mall 出口驶出,驾车 4.5 英里上山。在杂货店旁的红绿灯处右转,然后在第一个路口左转。转入左侧大棕褐色房屋的车道,地址为 714 North Cedar
  • 启发式方法:找到我们寄给您的最后一封信。开车前往回程地址所在的城镇。当你到达城里时,问问别人我们的房子在哪里。每个人都认识我们——有人会很乐意为您提供帮助。如果您找不到任何人,请使用公用电话给我们打电话,我们会来找您。

使用隐喻可以帮助你深入了解编程中的问题和流程。使用它们来帮助你思考编程活动并帮助你构想更好的做事方式。随着时间的推移,使用隐喻来阐明软件开发过程的人将被认为比不使用隐喻的人更了解编程并更快地生成更好的代码。

比如你可以通过“设计模式”这个隐喻:
“这段代码核心思想是命令模式,但 xxx 和 xxx 与命令模式定义的有所不同” 。这样,阅读代码的人如果了解命令模式,那么他需要关注的点,就一下子从整个代码设计变为只有 xxx

#常见软件开发隐喻

有许多不同的隐喻来形容软件开发的过程,有认为它是“写作的”,有认为它是 “种植的”,有认为是 “建造的“ 等等。这一节就是来分析这些隐喻哪个更好。

#写作

软件开发最原始的比喻源于“写代码”这一表达,将开发看作是一种写作。

写作比喻表明,开发一个程序就像写一封随意的信——你坐下来,拿着笔、墨水和纸,从头到尾写下来。它不需要任何正式的计划,你可以边走边想出你想说什么。

对于个人或小型项目,写作的比喻是有效的,但对于大型项目而言,则不够合适。

  • 写作通常是一项单人活动,而软件项目则很可能设计许多承担不同责任的人。
  • 当你写完一封信后,你将其放入信封并邮寄,这件活动就完成了,你不会也无法再修改它。而对于软件而言,典型的软件系统多达 90% 的开发工作是在初始版本发布后完成的。
  • 写作中,原创性非常重要。但在软件构件中,重用则非常重要。

但不幸的是,在《人月神话》中,作者说”计划扔掉一份代码,无论如何,你会的“。这就让人想起写了一半的草稿纸被扔进废纸篓的画面。
草稿纸和废纸篓

所以《人月神话》一定程度上加强了程序员将软件视作”写作“的可能。但写作被丢掉的成本很低(当你向你的阿姨写一份问候信,当你觉得不合适,你可以随时重新写一份),但软件开发的成本则极高。

#种植

一些软件开发人员表示,您应该将创建软件想象成种植农作物。设计一个片段,编写一个片段,测试一个片段,然后一次一点地将其添加到系统中。通过采取小步骤,你可以最大限度地减少随时可能遇到的麻烦。

但这个比喻实际上很糟糕,虽然“一次做一点”的想法和农务缓慢生长的方式有一点点类似,但也很难将农业的主要特征概括为“一次只做一点事”。

  • 农业上可以通过土地管理来增加产量(让土地休息一年增加土中的氮含量),但很难说让硬盘工作一年,以生成更多的代码。
  • 种植的比喻还隐含着“开发者对开发结果没有直接控制权”的意思。程序员无法在春天种下代码种子,然后在秋天获得丰富的代码。

#生长

将软件的构建隐喻为生长(Growing),可以替代之前的种植隐喻。

在增量开发中,你可以先制作一个可运行的最简系统。它不必接受真实的输入,不必对数据执行真实的操作,也不必产生真实的输出——它只需要成为一个足够强大的骨架,能够在开发时支撑真实的系统。这个基本的开始就像牡蛎中未来会增生为珍珠的小沙粒。

形成骨架后,逐渐放置肌肉和皮肤。将每个虚拟类更改为真实类,将虚假的输入改成真实的输入,将虚假的输出改成真实的输出。你一次添加一点代码,直到拥有一个完全工作的系统。

#建造

建造( Building)比前面的写作和生长更合适,它与增长的理念兼容(建造也是一点点建造的),并提供了更详细的指导。构建意味着规划、准备和执行的各个阶段,这些阶段的种类和程度取决于所构建的内容。当你探索这个比喻时,你会发现许多其他相似之处。

#制造过程的类似

如果你要建造的是一个简单结构(比如狗舍),你去买一些木材和钉子,花一下午的时间,就能做出来。

但如果你要建的是房屋,那建造过程会更加复杂

  • 你必须决定你想要建造什么样的房子:与软件开发中的问题定义类似
  • 你和建筑师必须提出总体设计并获得批准:与软件架构设计类似
  • 你需要绘制详细的蓝图并雇用承包商:与详细的软件设计类似
  • 准备好建筑工地,打好地基,搭建房屋框架,放置壁板和屋顶,然后安装管道和电线:与软件构建类似
  • 当房子的大部分完工后,园林设计师、油漆工和装饰师就会进来,充分利用你的财产和你建造的房屋:与软件优化类似
  • 在整个过程中,各种检查人员都会来检查场地、基础、框架、布线和其他检查项:与软件审查和测试类似

#重来的成本

如果你忘了给狗舍做门,或者犯了一些其他错误,这不是啥大问题。你可以尝试去修复,大不了重头再来,你浪费的只不过是一下午的时间和一点钱。这与写小型软件非常类似,如果你写了 1000 行代码后发现对需求的理解错误,或用了错误的设计,你可以重构或直接重写,影响都不是太大。

但更大的复杂性和规模意味着这两项活动的后果更大:

  • 建房子,材料有些贵,但最主要的支出是人工。拆掉一堵墙并将其移动六英寸是昂贵的,不是因为你浪费了很多钉子,而是因为你必须向人们支付移动墙所需的额外时间。
  • 在构建软件产品时,材料甚至更便宜(电脑和键盘),但劳动力成本同样高。

#重用

在建造房屋时,你不会尝试建造可以购买的已经建成的东西:除非你是一个机械天才,不然你肯定会选择买现成的冰箱,而不是自己造一个。

  • 如果你正在构建软件系统,你也会做同样的事情。你将广泛使用高级语言功能,而不是编写自己的操作系统级代码。你还会使用各种已经构建的帮助类。

如果您要建造一座配有一流家具的豪华房屋,你可能会定制橱柜,或定制不同寻常形状和尺寸的窗户

  • 这种定制与软件开发有相似之处。如果你正在构建一流的软件产品,你可能会构建自己的帮助类或底层库以获得更好的速度或准确性

#规划

建筑施工和软件建设都受益于适当水平的规划。如果以错误的顺序构建软件,则很难编码、测试和调试。它可能需要更长的时间才能完成,或者项目可能会崩溃。因为每个模块都太复杂,因此当他们组合在一起时,可能会出现混乱。

周密的计划并不一定意味着详尽的计划或过度计划。你可以规划结构支撑,然后决定是否铺硬木地板或地毯、墙壁涂什么颜色、使用什么屋顶材料等等。

  • 一个精心计划的项目可以提高你以后改变对细节的想法的能力。
  • 你对正在构建的软件类型的经验越多,你就越能理所应当的确认某些细节。

#规格

在建筑过程中,如果你要建造仓库或工具棚,与建造医疗中心或核反应堆相比,你会使用不同级别的规划。

在软件中,你通常可能使用灵活的、轻量级的开发方法,但有时你需要严格的、重量级的方法来实现要求更高的软件。

#功能的重要性

如果墙壁是承重的,那么将墙壁移动六英寸的成本会比它只是房间之间的隔断要高。同样,在程序中进行架构更改比添加或删除需求功能的成本更高。

#额外预算

由于超大型结构的失效后果非常严重,因此必须对结构进行过度设计。如同建筑商仔细制定和检查他们的计划。它们建立了安全边际;多花 10% 的钱购买更坚固的材料,这总比让摩天大楼倒塌要好。

#工具箱

还有一种好隐喻,是不将技术视为规则,而视为分析工具:

  • 一个好的工匠知道适合工作的正确工具,并且知道如何正确使用它。
  • 程序员也是如此。你对编程了解得越多,你的工具箱就越充满分析工具,你就越知道何时使用它们,以及如何正确的使用它们。

在软件领域,有时候有人会告诉只使用某种软件方法,而排除其他方法。但如果你 100% 相信任何一种方法,你就会通过该方法来看待整个世界。在某些情况下,你会错过使用更适合你当前问题的方法。就如同如果你只拿着锤子,你可能会错误的将螺丝当成钉子。

工具箱比喻有助于正确看待所有方法、技术和技巧,以便在适当的时候使用。

#结合隐喻

因为隐喻是启发式的而不是算法式的,所以它们并不相互排斥。你可以使用任何能够激发你自己或团队内其他人思考的隐喻或隐喻的组合。

使用隐喻是一件模糊的事情。你必须扩展它们才能从它们提供的启发式见解中受益。但如果你把它们延伸得太远或方向错误,它们就会误导你,正如你可以滥用任何强大的工具一样。