Unix哲學起源於Ken Thompson早期關於如何設計一個服務接口簡潔、小巧精幹的操做系統的思考,隨着Unix文化在學習如何儘量發掘Thompson設計思想的過程當中不斷成長,同時一路上還從其它許多地方博採衆長。前端
Unix哲學說來不算是一種正規設計方法。它並不打算從計算機科學的理論高度來產生理論上完美的軟件。那些毫無動力、鬆鬆垮垮並且薪水微薄的程序員們,能在短短時間限內,如同神靈附體般造出穩定而新穎的軟件——這只不過是經理人永遠的夢囈罷了。linux
Unix哲學(同其它工程領域的民間傳統同樣)是自下而上的,而不是自上而下的。Unix哲學注重實效,立足於豐富的經驗。你不會在正規方法學和標準中找到它,它更接近於隱性的半本能的知識,即Unix文化所傳播的專業經驗。它鼓勵那種分清輕重緩急的感受,以及懷疑一切的態度,並鼓勵你以幽默達觀的態度對待這些。程序員
Unix管道的發明人、Unix傳統的奠定人之一Doug McIlroy在[McIlroy78]中曾經說過:算法
(i)讓每一個程序就作好一件事。若是有新任務,就從新開始,不要往原程序中加入新功能而搞得複雜。shell
(ii)假定每一個程序的輸出都會成爲另外一個程序的輸入,哪怕那個程序仍是未知的。輸出中不要有無關的信息干擾。避免使用嚴格的分欄格式和二進制格式輸入。不要堅持使用交互式輸入。編程
(ⅲ)儘量早地將設計和編譯的軟件投入試用, 哪怕是操做系統也不例外,理想狀況下, 應該是在幾星期內。對拙劣的代碼別猶豫,扔掉重寫。後端
(iv)優先使用工具而不是拙劣的幫助來減輕編程任務的負擔。工欲善其事,必先利其器。數組
後來他這樣總結道(引自《Unix的四分之一世紀》(A Quarter Century of Unix [Salus])):瀏覽器
Unix哲學是這樣的:一個程序只作一件事,並作好。程序要能協做。程序要能處理文本流,由於這是最通用的接口。網絡
Rob Pike, 最偉大的C語言大師之一, 在《Notes on C Programming》中從另外一個稍微不一樣的角度表述了Unix的哲學[Pike]:
原則1:你沒法判定程序會在什麼地方耗費運行時間。瓶頸常常出如今想不到的地方,因此別急於胡亂找個地方改代碼,除非你已經證明那兒就是瓶頸所在。
原則2:估量。在你沒對代碼進行估量,特別是沒找到最耗時的那部分以前,別去優化速度。
原則3:花哨的算法在n很小時一般很慢,而n一般很小。花哨算法的常數複雜度很大。除非你肯定n老是很大,不然不要用花哨算法(即便n很大,也優先考慮原則2)。
原則4:花哨的算法比簡單算法更容易出bug、更難實現。儘可能使用簡單的算法配合簡單的數據結構。
原則5:數據壓倒一切。若是已經選擇了正確的數據結構而且把一切都組織得層次分明,正確的算法也就不言自明。編程的核心是數據結構,而不是算法。
原則6:沒有原則6。
Ken Thompson——Unix最第一版本的設計者和實現者,禪宗偈語般地對Pike的原則4做了強調:
拿不許就窮舉。
Unix哲學中更多的內容不是這些先哲們口頭表述出來的,而是由他們所做的一切和Unix自己所做出的榜樣體現出來的。從總體上來講,能夠歸納爲如下幾點:
1. 模塊原則:使用簡潔的接口拼合簡單的部件。
2. 清晰原則:清晰勝於機巧。
3. 組合原則:設計時考慮拼接組合。
4. 分離原則:策略同機制分離,接口同引擎分離。
5. 簡潔原則:設計要簡潔,複雜度能低則低。
6. 吝嗇原則:除非確無它法,不要編寫龐大的程序。
7. 透明性原則:設計要可見,以便審查和調試。
8. 健壯原則:健壯源於透明與簡潔。
9. 表示原則:把知識疊入數據以求邏輯質樸而健壯。
10. 通俗原則:接口設計避免標新立異。
11. 緘默原則:若是一個程序沒什麼好說的,就沉默。
12. 補救原則:出現異常時,立刻退出並給出足夠錯誤信息。
13. 經濟原則:寧花機器一分,不花程序員一秒。
14. 生成原則:避免手工hack,儘可能編寫程序去生成程序。
15. 優化原則:雕琢前先要有原型,跑以前先學會走。
16. 多樣原則:決不相信所謂「不二法門」的斷言。
17. 擴展原則:設計着眼將來,將來總比預想來得快。
若是剛開始接觸Unix,這些原則值得好好體味一番。談軟件工程的文章經常會推薦大部分的這些原則,可是大多數其它操做系統缺少恰當的工具和傳統將這些準則付諸實踐,因此,多數的程序員還不能自始至終地貫徹這些原則。蹩腳的工具、糟糕的設計、過分的勞做和臃腫的代碼對他們已是屢見不鮮了;他們奇怪,Unix的玩家有什麼好煩的呢。
1.6.1 模塊原則:使用簡潔的接口拼合簡單的部件
正如Brian Kernighan曾經說過的:「計算機編程的本質就是控制複雜度」[Kernighan-Plauger]。排錯佔用了大部分的開發時間,弄出一個拿得出手的可用系統,一般與其說出自才華橫溢的設計成果,還不如說是跌跌撞撞的結果。
彙編語言、編譯語言、流程圖、過程化編程、結構化編程、所謂的人工智能、第四代編程語言、面向對象、以及軟件開發的方法論,不可勝數的解決之道被拋售者吹得神乎其神。但實際上這些都用處不大,緣由偏偏在於它們「成功」地將程序的複雜度提高到了人腦幾乎不能處理的地步。就像Fred Brooks的一句名言[Brooks]:沒有萬能藥。
要編制複雜軟件而又不至於一敗塗地的惟一方法就是下降其總體複雜度——用清晰的接口把若干簡單的模塊組合成一個複雜軟件。如此一來,多數問題只會侷限於某個局部,那麼就還有但願對局部進行改進而不至牽動全身。
1.6.2 清晰原則: 清晰勝於機巧
維護如此重要而成本如此高昂;在寫程序時,要想到你不是寫給執行代碼的計算機看的,而是給人——未來閱讀維護源碼的人,包括你本身——看的。
在Unix傳統中,這個建議不只意味着代碼註釋。良好的Unix實踐一樣信奉在選擇
算法和實現時就應該考慮到未來的可擴展性。而爲了取得程序一丁點的性能提高就大幅度增長技術的複雜性和晦澀性,這個買賣作不得——這不只僅是由於複雜的代碼容易滋生bug,也由於它會使往後的閱讀和維護工做更加艱難。
相反,優雅而清晰的代碼不只不容易崩潰——並且更易於讓後來的修改者馬上理解。這點很是重要,尤爲是說不定若干年後回過頭來修改這些代碼的人可能偏偏就是你本身。
永遠不要去吃力地解讀一段晦澀的代碼三次。第一次也許僥倖成功,但若是發現必須從新解讀一遍——離第一次過久了,具體細節無從回想——那麼你該註釋代碼了,這樣第三次就相對不會那麼痛苦了。
—Henry Spencer
1.6.3 組合原則:設計時考慮拼接組合
若是程序彼此之間不能有效通訊,那麼軟件就不免會陷入複雜度的泥淖。
在輸入輸出方面,Unix傳統極力提倡採用簡單、文本化、面向流、設備無關的格式。在經典的Unix下,多數程序都儘量採用簡單過濾器的形式,即將一個輸入的簡單文本流處理爲一個簡單的文本流輸出。
拋開世俗眼光,Unix程序員偏心這種作法並非由於他們仇視圖形用戶界面,而是由於若是程序不採用簡單的文本輸入輸出流,它們就極難銜接。
Unix中,文本流之於工具,就如同在面向對象環境中的消息之於對象。文本流界面的簡潔性增強了工具的封裝性。而許多精緻的進程間通信方法,好比遠程過程調用,都存在牽扯過多各程序間內部狀態的傾向。
要想讓程序具備組合性,就要使程序彼此獨立。在文本流這一端的程序應該儘量不要考慮文本流另外一端的程序。將一端的程序替換爲另外一個大相徑庭的程序,而徹底不驚擾另外一端應該很容易作到。
GUI能夠是個好東西。有時竭盡所能也不可避免複雜的二進制數據格式。可是,在作一個GUI前,最好仍是應該想一想可不能夠把複雜的交互程序跟幹粗活的算法程序分離開,每一個部分單獨成爲一塊,而後用一個簡單的命令流或者是應用協議將其組合在一塊兒。
在構思精巧的數據傳輸格式前,有必要實地考察一下,是否能利用簡單的文本數據格式;以一點點格式解析的代價,換得可使用通用工具來構造或解讀數據流的好處是值得的。
當程序沒法天然地使用序列化、協議形式的接口時,正確的Unix設計至少是,把儘量多的編程元素組織爲一套定義良好的API。這樣,至少你能夠經過連接調用應用程序,或者能夠根據不一樣任務的需求粘合使用不一樣的接口。
(咱們將在第7章詳細討論這些問題。)
1.6.4 分離原則: 策略同機制分離,接口同引擎分離
在Unix之失的討論中,咱們談到過X系統的設計者在設計中的基本抉擇是實行「機制,而不是策略」這種作法——使X成爲一個通用圖形引擎,而將用戶界面風格留給工具包或者系統的其它層次來決定。這一點得以證實是正確的,由於策略和機制是按照不一樣的時間尺度變化的,策略的變化要遠遠快於機制。GUI工具包的觀感時尚來去匆匆,而光柵操做和組合倒是永恆的。
因此,把策略同機制揉成一團有兩個負面影響:一來會使策略變得死板,難以適應用戶需求的改變,二來也意味着任何策略的改變都極有可能動搖機制。
相反,將二者剝離,就有可能在探索新策略的時候不足以打破機制。另外,咱們也能夠更容易爲機制寫出較好的測試(由於策略過短命,不值得花太多精力在這上面)。
這條設計準則在GUI環境以外也被普遍應用。總而言之,這條準則告訴咱們應該設法將接口和引擎剝離開來。
實現這種剝離的一個方法是,好比,將應用按照一個庫來編寫,這個庫包含許多由內嵌腳本語言驅動的C服務程序,而至於整個應用的控制流程則用腳原本撰寫而不是用C語言。這種模式的經典例子就是Emacs編輯器,它使用內嵌的腳本語言Lisp解釋器來控制用C編寫的編輯原語操做。咱們會在第11章討論這種設計風格。
另外一個方法是將應用程序分紅能夠協做的前端和後端進程,經過套接字上層的專用應用協議進行通信;咱們會在第5章和第7章討論這種設計。前端實現策略,後端實現
機制。比起僅用單個進程的總體實現方式來講,這種雙端設計方式大大下降了總體複雜度,bug有望減小,從而下降程序的壽命週期成本。
1.6.5 簡潔原則:設計要簡潔,複雜度能低則低
來自多方面的壓力經常會讓程序變得複雜(由此代價更高,bug更多),其中一種壓力就是來自技術上的虛榮心理。程序員們都很聰明,經常以能玩轉複雜東西和耍弄抽象概念的能力爲傲,這一點也無可厚非。但正因如此,他們經常會與同行們比試,看看誰可以鼓搗出最錯綜複雜的美妙事物。正如咱們常常所見,他們的設計能力大大超出他們的實現和排錯能力,結果即是代價高昂的廢品。
「錯綜複雜的美妙事物」聽來自相矛盾。Unix程序員相互比的是誰可以作到「簡潔而漂亮」並以此爲榮,這一點雖然只是隱含在這些規則之中,但仍是很值得公開提出來強調一下。
—Doug McIlroy
更爲常見的是(至少在商業軟件領域裏),過分的複雜性每每來自於項目的要求,而這些要求經常基於當月的推銷熱點,而不是基於顧客的需求和軟件實際可以提供的功能。許多優秀的設計被市場推銷所須要的大堆大堆「特性清單」扼殺——實際上,這些特×××幾乎從未用過。而後,惡性循環開始了:比別人花哨的方法就是把本身變得更花哨。很快,龐大臃腫變成了業界標準,每一個人都在使用臃腫不堪、bug極多的軟件,連軟件開發人員也不敢敝帚自珍。
不管以上哪一種方式,最後每一個人都是失敗者。
要避免這些陷阱,惟一的方法就是鼓勵另外一種軟件文化,以簡潔爲美,人人對龐大複雜的東西羣起而攻之——這是一個很是看重簡單解決方案的工程傳統,老是設法將程序系統分解爲幾個可以協做的小部分,並本能地抵制任何用過多噱頭來粉飾程序的企圖。
這就有點Unix文化的意味了。
1.6.6 吝嗇原則: 除非確無它法,不要編寫龐大的程序
「大」有兩重含義:體積大,複雜程度高。程序大了,維護起來就困難。因爲人們對花費了大量精力才作出來的東西難以割捨,結果致使在龐大的程序中把投資浪費在註定要失敗或者並不是最佳的方案上。
(咱們會在第13章就軟件的最佳大小進行更多的詳細討論。)
1.6.7 透明性原則:設計要可見,以便審查和調試
由於調試一般會佔用四分之三甚至更多的開發時間,因此一開始就多作點工做以減小往後調試的工做量會很划算。一個特別有效的減小調試工做量的方法就是設計時充分考慮透明性和顯見性。
軟件系統的透明性是指你一眼就可以看出軟件是在作什麼以及怎樣作的。顯見性指程序帶有監視和顯示內部狀態的功能,這樣程序不只可以運行良好,並且還能夠看得出它以何種方式運行。
設計時若是充分考慮到這些要求會給整個項目全過程都帶來好處。至少,調試選項的設置應該儘可能不要在過後,而應該在設計之初便考慮進去。這是考慮到程序不但應該可以展現其正確性,也應該可以把原開發者解決問題的思惟模型告訴後來者。
程序若是要展現其正確性,應該使用足夠簡單的輸入輸出格式,這樣才能保證很容易地檢驗有效輸入和正確輸出之間的關係是否正確。
出於充分考慮透明性和顯見性的目的,還應該提倡接口簡潔,以方便其它程序對其進行操做——尤爲是測試監視工具和調試腳本。
1.6.8 健壯原則: 健壯源於透明與簡潔
軟件的健壯性指軟件不只能在正常狀況下運行良好,並且在超出設計者設想的意外條件下也可以運行良好。
大多數軟件禁不起磕碰,毛病不少,就是由於過於複雜,很難通盤考慮。若是不可以正確理解一個程序的邏輯,就不能確信其是否正確,也就不能在出錯的時候修復它。
這也就帶來了讓程序健壯的方法,就是讓程序的內部邏輯更易於理解。要作到這一點主要有兩種方法:透明化和簡潔化。
就健壯性而言,設計時要考慮到能承受極端大量的輸入,這一點也很重要。這時牢記組合原則會頗有益處;經不起其它一些程序產生的輸入(例如,原始的Unix C編譯器聽說須要一些小小的升級才能處理好Yacc的輸出)。固然,這其中涉及的一些形式對人類來講每每看起來沒什麼實際用處。好比,接受空的列表/字符串等等,即便在人們不多或者根本就不提供空字符串的地方也得如此,這能夠避免在用機器生成輸入時須要對這種狀況進行特殊處理。
—Henry Spencer
在有異常輸入的狀況下,保證軟件健壯性的一個至關重要的策略就是避免在代碼中出現特例。bug一般隱藏在處理特例的代碼以及處理不一樣特殊狀況的交互操做部分的代碼中。
上面咱們曾說過,軟件的透明性就是指一眼就可以看出來是怎麼回事。若是「怎麼回事」不算複雜,即人們不須要絞盡腦汁就可以推斷出全部可能的狀況,那麼這個程序就是簡潔的。程序越簡潔,越透明,也就越健壯.
模塊性(代碼簡樸,接口簡潔)是組織程序以達到更簡潔目的的一個方法。另外也有其它的方法能夠獲得簡潔。接下來就是另外一個。
1.6.9 表示原則: 把知識疊入數據以求邏輯質樸而健壯
即便最簡單的程序邏輯讓人類來驗證也很困難,可是就算是很複雜的數據,對人類來講,仍是相對容易地就可以推導和建模的。不信能夠試試比較一下,是五十個節點的指針樹,仍是五十行代碼的流程圖更清楚明瞭;或者,比較一下究竟用一個數組初始化器來表示轉換表,仍是用switch語句更清楚明瞭呢?能夠看出,不一樣的方式在透明性和清晰性方面具備很是顯著的差異。參見Rob Pike的原則5。
數據要比編程邏輯更容易駕馭。因此接下來,若是要在複雜數據和複雜代碼中選擇一個,寧願選擇前者。更進一步:在設計中,你應該主動將代碼的複雜度轉移到數據之中去。
此種考量並不是Unix社區的原創,可是許多Unix代碼都顯示受其影響。特別是C語言對指針使用控制的功能,促進了在內核以上各個編碼層面上對動態修改引用結構。在
結構中用很是簡單的指針操做就可以完成的任務,在其它語言中,每每不得不用更復雜的過程才能完成。
(咱們將在第9章再討論這些技術。)
1.6.10 通俗原則:接口設計避免標新立異
(也就是衆所周知的「最少驚奇原則」。)
最易用的程序就是用戶須要學習新東西最少的程序——或者,換句話說,最易用的程序就是最切合用戶已有知識的程序。
所以,接口設計應該避免毫無來由的標新立異和自做聰明。若是你編制一個計算器程序,‘+’應該永遠表示加法。而設計接口的時候,儘可能按照用戶最可能熟悉的一樣功能接口和類似應用程序來進行建模。
關注目標受衆。他們也許是最終用戶,也許是其餘程序員,也許是系統管理員。對於這些不一樣的人羣,最少驚奇的意義也不一樣。
關注傳統慣例。Unix世界造成了一套系統的慣例,好比配置和運行控制文件的格式,命令行開關等等。這些慣例的存在有個極好的理由:緩和學習曲線。應該學會並使用這些慣例。
(咱們將在第5章和第10章討論這些傳統慣例。)
最小立異原則的另外一面是避免表象類似而實際卻略有不一樣。這會極端危險,由於表象類似每每致使人們產生錯誤的假定。因此最好讓不一樣事物有明顯區別,而不要看起來幾乎如出一轍。
—Henry Spencer
1.6.11 緘默原則:若是一個程序沒什麼好說的,就保持沉默
Unix中最古老最持久的設計原則之一就是:若程序沒有什麼特別之處可講,就保持沉默。行爲良好的程序應該默默工做,決不嘮嘮叨叨,礙手礙腳。沉默是金。
「沉默是金」這個原則的起始是源於Unix誕生時尚未視頻顯示器。在1969年的緩慢的打印終端,每一行多餘的輸出都會嚴重消耗用戶的寶貴時間。如今,這種狀況已不復存在,一切從簡的這個優良傳統流傳至今。
我認爲簡潔是Unix程序的核心風格。一旦程序的輸出成爲另外一個程序的輸入,就很容易把須要的數據挑出來。站在人的角度上來講――重要信息不該該混雜在冗長的程序內部行爲信息中。若是顯示的信息都是重要的,那就不用找了。
—Ken Arnold
設計良好的程序將用戶的注意力視爲有限的寶貴資源,只有在必要時纔要求使用。
(咱們將在第11章末尾進一步討論緘默原則及其理由。)
1.6.12 補救原則: 出現異常時,立刻退出並給出足量錯誤信息
軟件在發生錯誤的時候也應該與在正常操做的狀況下同樣,有透明的邏輯。最理想的狀況固然是軟件可以適應和應付非正常操做;而若是補救措施明明沒有成功,卻悄無聲息地埋下崩潰的隱患,直到好久之後才顯現出來,這就是最壞的一種狀況。
所以,軟件要儘量從容地應付各類錯誤輸入和自身的運行錯誤。可是,若是作不到這一點,就讓程序儘量以一種容易診斷錯誤的方式終止。
同時也請注意Postel的規定[8]:「寬容地收,謹慎地發」。Postel談的是網絡服務程序,可是其含義能夠廣爲適用。就算輸入的數據很不規範,一個設計良好的程序也會盡可能領會其中的意義,以儘可能與別的程序協做;而後,要麼響亮地倒塌,要麼爲工做鏈下一環的程序輸出一個嚴謹乾淨正確的數據。
然而,也請注意這條警告:
最初HTML文檔推薦「寬容地接受數據」,結果由於每一種瀏覽器都只接受規範中一個不一樣的超集,使咱們一直倍感無奈。要寬容的應該是規範而不是它們的解釋工具。
—Doug McIlroy
McIlroy 要求咱們在設計時要考慮寬容性,而不是用過度縱容的實現來補救標準的不足。不然,正如他所指出的同樣,一不留神你會死得很難看。
1.6.13 經濟原則: 寧花機器一分,不花程序員一秒
在Unix早期的小型機時代,這一條觀點仍是至關激進的(那時機器要比如今慢得多也貴得多)。現在,隨着技術的發展,開發公司和大多數用戶(那些須要對核爆炸進行建模或處理三維電影動畫的除外)都可以獲得廉價的機器,因此這一準則的合理性就顯然不用多說啦!
但不知何故,實踐彷佛還沒徹底跟上現實的步伐。若是咱們在整個軟件開發中很嚴格的遵循這條原則的話,大多數的應用場合都應該使用高一級的語言,如Perl、Tcl、Python、Java、Lisp,甚至shell——這些語言能夠將程序員從自行管理內存的負擔中解放出來(參見[Ravenbrook])。
這種作法在Unix世界中已經開始施行,儘管Unix以外的大多數軟件商仍堅持採用舊Unix學派的C(或C++)編碼方法。本書會在後面詳細討論這個策略及其利弊權衡。
另外一個能夠顯著節約程序員時間的方法是:教會機器如何作更多低層次的編程工做,這就引出了……
1.6.14 生成原則: 避免手工hack,儘可能編寫程序去生成程序
衆所周知,人類很不善於幹辛苦的細節工做。所以,程序中的任何手工hacking都是滋生錯誤和延誤的溫牀。程序規格越簡單越抽象,設計者就越容易作對。由程序生成代碼幾乎(在各個層次)老是比手寫代碼廉價而且更值得信賴。
咱們都知道確實如此(畢竟這就是爲何會有編譯器、解釋器的緣由),但咱們卻經常不去考慮其潛在的含義。對於代碼生成器來講,須要手寫的重複而麻木的高級語言代碼,與機器碼同樣是能夠批量生產的。當代碼生成器可以提高抽象度時——即當生成器的說明性語句要比生成碼簡單時,使用代碼生成器會很合算,而生成代碼後就根本無需再費力地去手工處理了。
在Unix傳統中,人們大量使用代碼生成器使易於出錯的細節工做自動化。Parser/Lexer生成器就是其中的經典例子,而makefile生成器和GUI界面式的構建器(interface builder)則是新一代的例子。
(咱們會在第9章討論這些技術。)
1.6.15 優化原則: 雕琢前先得有原型,跑以前先學會走
原型設計最基本的原則最初來自於Kernighan 和 Plauger 所說的「90%的功能如今能實現,比100%的功能永遠實現不了強」。作好原型設計能夠幫助你避免爲蠅頭小利而投入過多的時間。
因爲略微不一樣的一些緣由,Donald Knuth(程序設計領域中屈指可數的經典著做之一《計算機程序設計藝術》的做者)廣爲傳播普及了這樣的觀點:「過早優化是萬惡之源」[9]。他是對的。
還不知道瓶頸所在就匆忙進行優化,這多是惟一一個比亂加功能更損害設計的錯誤。從畸形的代碼到雜亂無章的數據佈局,犧牲透明性和簡潔性而片面追求速度、內存或者磁盤使用的後果隨處可見。滋生無數bug,耗費以百萬計的人時——這點芝麻大的好處,遠不能抵消後續排錯所付出的代價。
常常使人不安的是,過早的局部優化實際上會妨礙全局優化(從而下降總體性能)。在總體設計中能夠帶來更多效益的修改經常會受到一個過早局部優化的干擾,結果,出來的產品既性能低劣又代碼過於複雜。
在Unix世界裏,有一個很是明確的悠久傳統(例證之一是Rob Pike以上的評論, 另外一個是Ken Thompson關於窮舉法的格言):先製做原型,再精雕細琢。優化以前先確保能用。或者:先能走,再學跑。「極限編程」宗師Kent Beck從另外一種不一樣的文化將這一點有效地擴展爲:先求運行,再求正確,最後求快。
全部這些話的實質實際上是一個意思:先給你的設計作個未優化的、運行緩慢、很耗內存可是正確的實現,而後進行系統地調整,尋找那些能夠經過犧牲最小的局部簡潔性而得到較大性能提高的地方。
製做原型對於系統設計和優化一樣重要——比起閱讀一個冗長的規格說明,判斷一個原型到底是不是符合設想要容易得多。我記得Bellcore有一位開發經理,他在人們尚未談論「快速原型化」和「敏捷開發」前好幾年就反對所謂的「需求」文化。他從不提交冗長的規格說明,而是把一些shell腳本和awk代碼結合在一塊兒,使其基本可以完成所須要的任務,而後告訴客戶派幾個職員來使用這些原型,問他們是否喜歡。若是喜歡,他就會說「在多少多少個月以後,花多少多少的錢就能夠得到一個商業版本」。他的估計每每很精確,但因爲當時的文化,他仍是輸給了那些相信需求分析應該主導一切的同行。
—Mike Lesk
藉助原型化找出哪些功能沒必要實現,有助於對性能進行優化;那些不用寫的代碼顯然無需優化。目前,最強大的優化工具恐怕就是delete鍵了。
我最有成效的一天就是扔掉了1000行代碼。
—Ken Thompson
(咱們將在第12章對相關內容進行深一步討論。)
1.6.16 多樣原則:決不相信所謂「不二法門」的斷言
即便最出色的軟件也經常會受限於設計者的想象力。沒有人能聰明到把全部東西都最優化,也不可能預想到軟件全部可能的用途。設計一個僵化、封閉、不肯與外界溝通的軟件,簡直就是一種病態的傲慢。
所以, 對於軟件設計和實現來講,Unix傳統有一點很好,即從不相信任何所謂的「不二法門」。Unix奉行的是普遍採用多種語言、開放的可擴展系統和用戶定製機制。
1.6.17 擴展原則: 設計着眼將來,將來總比預想快
若是說相信別人所宣稱的「不二法門」是不明智的話,那麼堅信本身的設計是「不二法門」簡直就是愚蠢了。決不要認爲本身找到了最終答案。所以,要爲數據格式和代
碼留下擴展的空間,不然,就會發現本身經常被原先的不明智選擇捆住了手腳,由於你沒法既要改變它們又要維持對原來的兼容性。
設計協議或是文件格式時,應使其具備充分的自描述性以即可以擴展。一直,老是,要麼包含進一個版本號,要麼採用獨立、自描述的語句,按照能夠隨時插入新的、換掉舊的而不會搞亂格式讀取代碼的方法組織格式。Unix經驗告訴咱們:稍微增長一點讓數據部署具備自描述性的開銷,就能夠在無需破壞總體的狀況下進行擴展,你的付出也就獲得了成千倍的回報。
設計代碼時,要有很好的組織,讓未來的開發者增長新功能時無需拆毀或重建整個架構。固然這個原則並非說你能隨意增長根本用不上的功能,而是建議在編寫代碼時要考慮到未來的須要,使之後增長功能比較容易。程序接合部要靈活, 在代碼中加入「若是你須要……」的註釋。有義務給以後使用和維護本身編寫的代碼的人作點好事。
也許未來就是你本身來維護代碼,而在最近項目的壓力之下你極可能把這些代碼都遺忘了一半。因此,設計爲未來着眼,節省的有可能就是本身的精力。
全部的Unix哲學濃縮爲一條鐵律,那就是各地編程大師們奉爲圭臬的「KISS」原則: