圍繞着軟件開發的使人困惑的隱喻愈來愈多。
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
)。
■
隱喻把軟件開發過程與其餘你熟系的活動聯繫在一塊兒,幫助你更好地理解。
■
有些隱喻比其餘一些隱喻更貼切。
■
經過把軟件的構建過程比做是房屋的建設過程,咱們能夠發現,仔細的準備是必要的,而大型項目和小型項目之間也是有差別的。
■
經過把軟件開發中的實踐比做是智慧工具箱中的工具,咱們又發現,每位程序員都有許多工具,但並不存在任何一個能適用於全部工做的工具,因地制宜地選擇正確工具是成爲能有效編程的程序員的關鍵。
■
不一樣的隱喻彼此並不排斥,應當使用對你最有益處的某種隱喻組合