围绕着软件开发的使人困惑的隐喻愈来愈多。
David Gries
说编写软件是一门科学(
a science
)(
1981
);而
Donald Knuth
说它是艺术(
an art
)(
1998
);
Watts Humphrey
则说它是一个过程(
a process
)(
1989
);
P. J. Plauger
和
Kent Beck
都说它就像是驾驶汽车(
driving a car
)——可他们两个却几乎得出了彻底相反的结论(
Plauger 1993
,
Beck 2000
)。
Alistair Cockburn
说它是一场游戏(
a game
)(
2002
);
Eric Raymond
又说它就如同是一个集市(
bazaar
)(
2000
);
Andy Hunt
和
Dave Thomas
说它就像园艺(
gardening
)同样;
Paul Heckel
则说它就像是拍摄《白雪公主和七个小矮人》(
1994
);而
Fred Brooks
说它像耕田、像捕猎、或像是跟恐龙一块儿淹死在“焦油坑”里面(
1995
)……到底哪个隐喻最好呢?
Software Penmanship: Writing Code
软件中的书法:写做代码
关于软件开发的最原始的隐喻是从“写做(
writing
)代码”这个说法发展出来的,这一隐喻暗示着开发一个程序就像写一封有原因的信同样——坐下来,拿出文房四宝,从头写到尾就完了。这时不须要正规地作计划,你想到什么东西把它写出来就是了。
许多的想法就是从写做这个隐喻衍生而来的。好比
Jon Bentley
说,你应该能够坐在火炉边上,品一杯白兰地或抽一口上好的雪茄,边上坐着你心爱的猎犬,去品味一段“深奥的程序”,就像面对的是一本出色的小说那样。
Brian Kernighan
和
P. J. Plauger
参考一本关于写做体裁的书《文体的要素》(《
The Elements of Style
》,
Strunk and White 2000
)
,将他们关于编程风格(
programming style
)的书命名为《编程风格的要素》(《
The Elements of Programming Style
》,
1978
)。程序员们也常常会讨论“程序的可读性(
readability
)”。
对于我的规模的工做乃至小型的项目来讲,这种写信的隐喻已经足够了,然而对于其余场合而言,这个隐喻还远远不够——它没有完整、充分地刻画软件开发工做。书写一般只是我的的活动,而一个软件项目多半会涉及承担许多不一样职责的不少人。在你写完一封信以后,你只要把它塞进信封而后寄出去就完了,你不再能修改它——从任何程度和目的上看,这件事情都已经结束了。而软件的修改没那么难,也很难说有真正彻底结束的时候。典型的软件系统在其首次发布以后的工做量,可能达到整个工做量的
90%
,典型状况下也有三分之二之多(
Pigoski 1997
)。对写做而言,最重要的是其原创性。可是对于软件构建来讲,“努力创造真正的原创成果”的开发效率,每每低于专一于重用(
reuse
)以往项目的一些设计思想、代码以及测试用例(
test case
)的开发效率。总之,写做这一隐喻所暗示的软件开发过程太过简单、太过呆板了。
然而不幸的是,这种用文字(信件)写做所作的隐喻经过一本在软件领域最为流行的著做之一——
Fred Brooks
的《人月神话》(《
The Mythical Man-Month
》,
Brooks 1995
)而变成了不朽的思想。
Brooks
说:“要计划抛弃一个,你一定会那样,不管如何。”这个咒语给了咱们一幅如图
2-1
所示的景象:被扔进纸篓里的成堆的半成品草稿。
图
2-1
文字写做这一隐喻暗示着软件开发过程是一种代价昂贵的试错(
trial and error
)过程,而非仔细的规划和设计
在给你叔叔写一封“最近好吗”这样的礼节性问候信时,“计划扔掉一张草稿”也许还比较实际。但若是将“书写软件
”这个隐喻引伸为“计划扔掉一个(软件)”,则不是一个好的建议——尤为是在软件的主要系统就已经花费了至关于一栋十层高的办公楼或一艘远洋客轮这么多成本时。要想中奖不难,只要你能忍受坐在你钟情的旋转***上转上无数圈就行。诀窍在于看成第一次尝试的时候就让它成功
——或者在成本最低的时候多试几回。其余一些隐喻更好地阐明了达到这个目标的途径。
Software Farming: Growing a System
软件的耕做法:培植系统
相对于前面那个呆板的用写做所作的隐喻,一些软件开发人员则认为应当将创造软件想象成相似播种和耕做的情形。你一次设计系统的一小部分、写出一段代码、作一点测试,并将成果一点点添加到整个系统中。经过这种小步前进,你能够把每次可能遇到的麻烦减到最小。
有时候人们会用很糟的隐喻去描述一种很好的技术,此时须要保全这一技术,并去寻找更好的隐喻。这个例子里的增量技术是颇有价值的,但把它比做播种和耕做却很是糟糕。
“每次作一点”这个主意可能在某些方面与农做物生长相似,但把软件开发类比为耕做就很不贴切,也没有太多意义,并且咱们很容易用下面即将介绍的更好的隐喻替代它。人们也很难把耕做这个隐喻引伸到“一次作一点事情”以外。若是你认同耕做这种隐喻,就请想象一下图
2-2
的状况:你会发现本身谈论的是:对系统计划施肥、对细节设计疏果,并经过有效的管理土地来增长代码的产量,最终取得代码大丰收。你还会说“轮种
C++
和大麦”,或者让土地闲置一年以增长硬盘里面氮肥的供应量。
软件耕做这一隐喻的弱点在于它暗示了人们将没法对开发软件的过程和方式进行任何直接的控制。你在春天播下代码的种子,而后按照农历节气向土地佬儿许几个愿,你将会在秋天收获到丰盛的代码。
图
2-2
很难将耕做这一隐喻恰当地引伸到软件开发领域
Software Oyster Farming: System Accretion
软件的牡蛎养殖观点:系统生长
在谈论培育(
growing
)软件的时候,有时人们其实是指软件的生长(
accretion
),这两种隐喻是紧密相关的,而软件生长是一幅更发人深省的景象。看到“生长”这个词,就算手头没有字典,咱们也都能明白它指的是经过外在的增长或吸取而逐渐地生长或变大。“生长”这个词描述了牡蛎制造珍珠的过程,逐渐地增添微量的碳酸钙。在地质学里,“
accretion
”一词的意思是“冲积层”,指的是水流中夹带的沉淀物的冲积而不断扩大的陆地。在正式的术语中,“冲积层”是指海岸沿线的陆地因受到水流冲击,水中夹带的物质不断沉积而造成的增加。
这里并非说要你学会如何从水流中夹带的沉积物中提炼出代码来,而是说你须要学会如何一次为软件系统增长一个小部分。跟“生长”密切相关的另外一些词语有:“增量的(
incremental
)”、“迭代的(
iterative
)”、“自适应的(
adaptive
)”以及“演进的(
evolutionary
)”。以增量方式进行设计、编译和测试,都是目前已知的最强有力的软件开发概念。
在进行增量式开发时,咱们先作出软件系统的一个尽量简单、但能运行的版本。它没必要接受真实的输入,也无须对数据进行真正的处理,更不用产生真实的输出——它仅仅须要构成一个足够强壮的骨架,支撑起将来将要开发的真实系统。对于你标志出的每一项基本功能,可能仅须要调用虚假的类(
dummy classes
)。这个最基本的起点,就像牡蛎开始孕育珍珠的那颗细小沙粒。
在骨架造成以后,你要一点点地在其上附着肌肉和皮肤:把每一个虚假的类替换为真正的类;再也不伪装接受输入,而是把接收真实输入的代码替换进去;再也不伪装产生输出,而是把产生真实输出的代码替换进去。你一次增长一小部分代码,直到获得一个彻底能够工做的系统。
支持这一方法的一件逸事或曰证据使人印象深入。那位在
1975
年建议咱们建造一份(软件)以备扔掉(
building one to throw away
)的
Fred Brooks
说,在他写完了里程碑式的著做《人月神话》以后的十年间,没有什么能像增量式开发那样完全地改变了他我的的开发习惯及其效力(
1995
)。
Tom Gilb
在他突破性的著做《软件工程管理原理》(
The Principles of Software Engineering Management
,
1988
)中也一样指出了这一点,该书介绍了演进式交付(
Evolutionary Delivery
),它在很大程度上奠基了现在敏捷编程(
agile programming
)方法的基础。眼下很多方法论都是基于这一理念(
Beck 2000
;
Cockburn 2002
;
Highsmith 2002
;
Reifer 2002
;
Martin 2003
;
Larman 2004
)。
做为一个隐喻而言,增量式开发的优点在于未作过分的承诺。比起耕做那个隐喻来,对它做不恰当地引伸要更困难一些。牡蛎孕育珍珠的图景也很好地刻画了增量式开发(或说生长)的情形。
Software Construction: Building Software
软件构建:建造软件
与“写做(
writing
)”软件或者“培育(
growing
)”软件而言,“建造(
building
)”软件的图景就更加有用了。它和软件生长的概念是相通的,且提供了更详细的指引。建造软件这一说法暗示了软件开发中存在着诸多阶段,如计划、准备及执行等,根据所建造软件的不一样,这些阶段的种类和程度可能会发生变化。进一步研究这一隐喻时,你还会发现许多其余方面的类似之处。
要搭一座四足的塔(
four-foot tower
),你要有一双稳健的手,要找一个平坦的表面,以及十来个无缺无损的啤酒罐。而要搭一座比它大
100
倍的塔,光是多
100
倍的啤酒罐还不够,还须要同时采用彻底不一样的计划方法和建造方法才行。
若是你要盖一个简单的建筑物——好比一个狗屋——你先开车到木材店买些木头和钉子。临近傍晚时分,你的爱犬
Fido
就有新窝了。若是你像图
2-3
那样忘了弄个门,或是犯了其余什么错误,那也没什么大不了的,修改一下或者干脆从头再来就是了。你的损失最多也就是一个下午的时间。这种宽松的方式对于小型的项目来讲也还算合适。若是你写
1 0
00
行的代码时采用了错误的设计,你还能够重构甚至从头再来,不会损失太多。
图
2-3
在简单结构上犯下错误,其惩罚也不过是一点时间,或是些许尴尬
若是你是在建一栋房子,那么这个建造过程就会复杂得多,而糟糕的设计所引起的后果也更严重。首先你要决定准备建一个什么类型的房子——在软件开发里的相似事项称为问题定义(
problem definition
)。接下来,你必须和某个建筑师(
architect
)探讨这一整体设计,并获得批准。这跟软件架构设计(
architectural design
)十分类似。而后你画出详细的蓝图,雇一个承包人。就像软件的详细设计。再而后,你要准备好建造地点,打好地基,搭建房屋框架,砌好边墙,盖好房顶,通好水、电、煤气等。这就如同是软件的构建(
construction
)同样。在房子大部分完成以后,庭院设计师、油漆匠和装修工还要来把你新盖的家以及里面的家什美化一番
软件的优化(
oprimization
)过程。在整个过程当中,还会有各类监查人员来检查工地、地基、框架、布线以及其余须要检查的地方。这至关于软件复查(评审,
reviews
)和审查(
inspections
)。
在这两种活动中,更高的复杂度和更大的规模都会带来更多的结论。盖房子的时候,建材多少也是有些昂贵,但主要的开销仍是在人力上。把一栋墙推倒而后移动半尺是很昂贵的,倒不在于浪费多少钉子,而是由于你要付给工人们更多的工钱,移动这堵墙耗费了额外的工时。你只有尽量地把房子设计好,就像图
2-4
那样,这样你才不用浪费时间去修正那些原本能够避免的错误。在开发一个软件产品时,原材料甚至更加廉价,但劳动力上的花销也更昂贵。变动一份报表的格式所要付出的代价,和移动房间里的一堵墙同样高昂,由于二者的主要成本构成部分都是花费人的时间。
图
2-4
更复杂的结构须要更加仔细地规划
除此以外,这两种活动还有什么类似之处呢?建造一个房子的时候,你不会去试着建造那些能买获得的现成的东西。你会买洗衣机、烘干机、洗碗机、电冰箱以及冷藏柜。除非你是机电方面的巫师,不然你是不会考虑本身动手弄这些东西的。你还会购买预先造好的橱柜、餐桌、门窗以及浴具,等等。当开发软件时,你也会这么作的。你会大量使用高级语言所提供的功能,而不会本身去编写操做系统层次的代码。你可能还要用些现成的程序库,好比说一些容器类(
container classes
)、科学计算函数、用户界面组件、数据库访问组件,等等。总之,本身编写那些能买获得的现成的代码一般是没有意义的。
但若是你要建造一间拥有一流家具的高档住宅,那你可能须要特别订制的橱柜,还可能须要能和这些橱柜相搭配的洗碗机、冰箱和冷藏柜等,也可能须要以特殊的形状和特别尺寸订制的窗户。在软件开发中也有和这种订制类似的状况。若是你要开发一款一流的软件产品,你可能会本身编写科学计算函数以便得到更快的速度和更高的精度。你还可能须要本身编写容器类、用户界面组件以及数据库访问组件等,这样作可让产品的各个部分无缝拼接,拥有一致的外观和体验。
适当的多层次的规划对于建造建筑物和构建软件都有好处。若是你按错误的顺序构建软件,那么编码、测试和调试都会更难。须要花更长的时间才能完成,甚至整个项目干脆就分崩离析了——因为每一个人的工做都过于复杂,全部成果组合在一块儿的时候就变得混乱不堪了。
精心计划,并不是意味着事无巨细的计划或者过分的计划。你能够把房屋结构性的支撑(
structural support
)规划清楚,而在往后再决定是用木地板仍是地毯,
墙面漆成什么颜色,屋顶使用什么材料,等等。一项规划得当的项目可以提高你“在后期改变细节(设计)”的能力。你对同类软件的开发经验越丰富,(在开发新软件时)就能认准更多的细节。你只须要保证已经作了足够的计划,不会到后来由于计划上不足而引起重大问题。
用建筑房屋来类比软件构建,还有助于解释为何不一样的软件项目能从不一样的开发方法中获益。建筑业中,盖间仓库或者工具房,或是一座医院或者核反应站,你在规划、设计及质量保证方面所需达到的程度是不同的。盖一座学校、一幢摩天大楼,或一座三居室的小别墅,所用的方法也不会相同。同理,在软件开发中,一般你只须要用灵活的、轻量级的(
lightweight
)方法,但有时你就必须得用严格的、重量级的开发方法,以达到所需的安全性目标或其余什么目标。
软件的变更在建筑领域也有相似事物。把一堵承重墙移动半尺所需花费的成本,确定要比仅仅移动一面隔墙更高。一样,对软件进行结构性的修改所需花费的成本,确定也比仅仅增删一些周边功能更高。
最后,建筑这一隐喻让人们对超大型的软件项目的认识更加深入。超大型的结构一旦出现问题,后果将很是严重,所以有必要对这样的结构进行超出常规的规划与建设(
over-engineered
)。建筑人员须要很是当心地制定并核查设计规划,在建设时留有余地以保障安全;宁肯多花
10%
的成本买更坚固的材料,也比摩天大楼倒下来要划算得多。还须要特别关注工做的时间。在建造帝国大厦(
The Empire Building
)的时候,每辆运料车运输时都留有
15
分钟的余地。若是某辆车没能在指定时间到位,则整个工期就会延误。
同理,对于超大型的软件项目,就须要比通常规模的项目有更高级别的规划设计。
Capers Jones
发表的报告称,一套
100
万行代码的软件系统,平均须要
69
种
文档(
1998
)。其需求规格文档通常有四五千页长,而设计文档经常是需求的两三倍长。不太可能有哪个人能彻底理解这种规模的项目的全部设计细节——甚至只是通读一遍都不那么容易。所以,更充分的准备工做也就理所应当了。
若是须要创造在经济规模上能够匹敌帝国大厦的庞大的软件项目,那么与之至关水准的技术与管理控制也是必需的。
按房屋建筑所做的这一隐喻,能够向许多其余方向引伸——这也是隐喻这一方法如此强有力的一个缘由。有不少常见的软件开发术语都是从建筑这一隐喻中衍生出来的:软件架构(建筑学,
architecture
)、支撑性测试代码(脚手架,
scaffolding
)、构建(建设,
construction
)、基础类(
foundation classes
)以及分离代码(
tearing code apart
)。你可能还据说过更多这一类的词语。
应用软件技术:智慧工具箱
能有效地开发高质量软件的人们,在终年累月中积累了大量的技术、技巧和诀窍。技术并非规矩(
rule
),它只是分析工具(
analytical tools
)。好的工匠知道完成某项工做要用哪样工具,也知道该怎样正确地使用。程序员也该这样。编程方面的知识学得越多,你脑中的工具箱中就会有更多的分析工具,也会知道该在什么时候用这些工具,以及怎样正确地使用它们。
在软件领域里,专业的咨询人员有时会让你专用某种软件开发方法而远离其余方法。这样并不稳当,由于当你百分之百地依赖于某一方法论时,你就只会用一种方法去看世界了。某些状况下,对于你所面临的问题还有其余更好的方法,你可能错失良机。这种“工具箱隐喻”可以帮助你把全部的方法、技术以及技巧留在脑海中——合适的时候便可拿来就用。
Combining Metaphors
组合各个隐喻
由于隐喻是一种启发式方法而不是算法,所以它们彼此并不排斥。你能够同时使用生长(
accretion
)和建筑(
construction
)这两个隐喻。你若是想用“写做”隐喻也行,你还能够把“写做”同“驾驶”、“狩猎狼人(
werewolf
)”、“与恐龙一块儿在焦油坑中淹死”等隐喻组合到一块儿。你能够选用任何一种隐喻或是一些隐喻的组合,只要它能激发你的思惟灵感,并让你和团队其余成员更好地沟通。
使用隐喻又是件说不清楚的事情(
fuzzy business
)。你须要适当地引伸它的含义,才能从其蕴含的深入启发中受益。但若你过度地或者在错误的方向上引伸了它的含义,它也会误导你。正如人们会误用任何强大的工具同样,你也可能误用隐喻,但它的强大的功效,还会成为你智慧工具箱中的一个宝贵部分。
Additional Resources
更多资源
在关于隐喻、模型(
model
)以及范型(
paradigm
)方面的众多书籍中,
Thomas Kuhn
写的那本是试金石。
Kuhn, Thomas S.
《科学变革的结构》(第三版)(
The Structure of Scientific Revolutions
, 3d ed. Chicago, IL: The University of Chicago Press, 1996.
)。
Kuhn
关于在一个达尔文周期中,科学理论如何相对于其余理论而诞生、发展并消亡的书,于
1962
年首次发布,奠基了科学哲学的基础。该书短小精悍,列举了大量科学中隐喻、模型以及范型间此消彼长的有趣示例。
Floyd, Robert W.
“编程范型”(“
The Paradigms of Programming.
”
1978
年图灵奖的颁奖演讲)。《
Communications of the ACM
》(《
ACM
通信》),
August 1979,
pp. 455
—
460.
这是一篇使人神往的关于软件开发中的模型的讨论,
Floyd
将
Kuhn
的理念应用到了编程上。
Key Points
要点
■
隐喻是启示而不是算法。所以它们每每有一点随意(
sloopy
)。
■
隐喻把软件开发过程与其余你熟系的活动联系在一块儿,帮助你更好地理解。
■
有些隐喻比其余一些隐喻更贴切。
■
经过把软件的构建过程比做是房屋的建设过程,咱们能够发现,仔细的准备是必要的,而大型项目和小型项目之间也是有差别的。
■
经过把软件开发中的实践比做是智慧工具箱中的工具,咱们又发现,每位程序员都有许多工具,但并不存在任何一个能适用于全部工做的工具,因地制宜地选择正确工具是成为能有效编程的程序员的关键。
■
不一样的隐喻彼此并不排斥,应当使用对你最有益处的某种隐喻组合