天天看點

Unix哲學

unix哲學起源于ken thompson早期關于如何設計一個服務接口簡潔、小巧精幹的作業系統的思考,随着unix文化在學習如何盡可能發掘thompson設計思想的過程中不斷成長,同時一路上還從其它許多地方博采衆長。

unix哲學說來不算是一種正規設計方法。它并不打算從計算機科學的理論高度來産生理論上完美的軟體。那些毫無動力、松松垮垮而且薪水微薄的程式員們,能在短短期限内,如同神靈附體般造出穩定而新穎的軟體——這隻不過是經理人永遠的夢呓罷了。

unix 哲學(同其它工程領域的民間傳統一樣)是自下而上的,而不是自上而下的。unix哲學注重實效,立足于豐富的經驗。你不會在正規方法學和标準中找到它,它 更接近于隐性的半本能的知識,即unix文化所傳播的專業經驗。它鼓勵那種厘清輕重緩急的感覺,以及懷疑一切的态度,并鼓勵你以幽默達觀的态度對待這些。

unix管道的發明人、unix傳統的創始者之一doug mcilroy在[mcilroy78]中曾經說過:

(i)讓每個程式就做好一件事。如果有新任務,就重新開始,不要往原程式中加入新功能而搞得複雜。

(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:資料壓倒一切。如果已經選擇了正确的資料結構并且把一切都組織得井井有條,正确的算法也就不言自明。程式設計的核心是資料結構,而不是算法[7]。

原則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的輸出)。當然,這其中涉及的一些形式對人類來說往往看起來沒什麼實際用處。比如,接受空的清單/字元 串等等,即使在人們很少或者根本就不提供空字元串的地方也得如此,這可以避免在用機器生成輸入時需要對這種情況進行特殊處理。

在有異常輸入的情況下,保證軟體健壯性的一個相當重要的政策就是避免在代碼中出現特例。bug通常隐藏在處理特例的代碼以及處理不同特殊情況的互動操作部分的代碼中。

上面我們曾說過,軟體的透明性就是指一眼就能夠看出來是怎麼回事。如果“怎麼回事”不算複雜,即人們不需要絞盡腦汁就能夠推斷出所有可能的情況,那麼這個程式就是簡潔的。程式越簡潔,越透明,也就越健壯.

子產品性(代碼簡樸,接口簡潔)是組織程式以達到更簡潔目的的一個方法。另外也有其它的方法可以得到簡潔。接下來就是另一個。

1.6.9 表示原則: 把知識疊入資料以求邏輯質樸而健壯

即 使最簡單的程式邏輯讓人類來驗證也很困難,但是就算是很複雜的資料,對人類來說,還是相對容易地就能夠推導和模組化的。不信可以試試比較一下,是五十個節點 的指針樹,還是五十行代碼的流程圖更清楚明了;或者,比較一下究竟用一個數組初始化器來表示轉換表,還是用switch語句更清楚明了呢?可以看出,不同 的方式在透明性和清晰性方面具有非常顯著的差别。參見rob pike的原則5。

資料要比程式設計邏輯更容易駕馭。是以接下來,如果要在複雜資料和複雜代碼中選擇一個,甯願選擇前者。更進一步:在設計中,你應該主動将代碼的複雜度轉移到資料之中去。

此種考量并非unix社群的原創,但是許多unix代碼都顯示受其影響。特别是c語言對指針使用控制的功能,促進了在核心以上各個編碼層面上對動态修改引用結構。在

結構中用非常簡單的指針操作就能夠完成的任務,在其它語言中,往往不得不用更複雜的過程才能完成。

(我們将在第9章再讨論這些技術。)

1.6.10 通俗原則:接口設計避免标新立異

(也就是衆所周知的“最少驚奇原則”。)

最易用的程式就是使用者需要學習新東西最少的程式——或者,換句話說,最易用的程式就是最切合使用者已有知識的程式。

是以,接口設計應該避免毫無來由的标新立異和自作聰明。如果你編制一個電腦程式,‘+’應該永遠表示加法。而設計接口的時候,盡量按照使用者最可能熟悉的同樣功能接口和相似應用程式來進行模組化。

關注目标閱聽人。他們也許是最終使用者,也許是其他程式員,也許是系統管理者。對于這些不同的人群,最少驚奇的意義也不同。

關注傳統慣例。unix世界形成了一套系統的慣例,比如配置和運作控制檔案的格式,指令行開關等等。這些慣例的存在有個極好的理由:緩和學習曲線。應該學會并使用這些慣例。

(我們将在第5章和第10章讨論這些傳統慣例。)

最小立異原則的另一面是避免表象相似而實際卻略有不同。這會極端危險,因為表象相似往往導緻人們産生錯誤的假定。是以最好讓不同僚物有明顯差別,而不要看起來幾乎一模一樣。

1.6.11   緘默原則:如果一個程式沒什麼好說的,就保持沉默

unix中最古老最持久的設計原則之一就是:若程式沒有什麼特别之處可講,就保持沉默。行為良好的程式應該默默工作,決不唠唠叨叨,礙手礙腳。沉默是金。

“沉默是金”這個原則的起始是源于unix誕生時還沒有視訊顯示器。在1969年的緩慢的列印終端,每一行多餘的輸出都會嚴重消耗使用者的寶貴時間。現在,這種情況已不複存在,一切從簡的這個優良傳統流傳至今。

我認為簡潔是unix程式的核心風格。一旦程式的輸出成為另一個程式的輸入,就很容易把需要的資料挑出來。站在人的角度上來說――重要資訊不應該混雜在冗長的程式内部行為資訊中。如果顯示的資訊都是重要的,那就不用找了。

—ken arnold

設計良好的程式将使用者的注意力視為有限的寶貴資源,隻有在必要時才要求使用。

(我們将在第11章末尾進一步讨論緘默原則及其理由。)

1.6.12 補救原則: 出現異常時,馬上退出并給出足量錯誤資訊

軟體在發生錯誤的時候也應該與在正常操作的情況下一樣,有透明的邏輯。最理想的情況當然是軟體能夠适應和應付非正常操作;而如果補救措施明明沒有成功,卻悄無聲息地埋下崩潰的隐患,直到很久以後才顯現出來,這就是最壞的一種情況。

是以,軟體要盡可能從容地應付各種錯誤輸入和自身的運作錯誤。但是,如果做不到這一點,就讓程式盡可能以一種容易診斷錯誤的方式終止。

同 時也請注意postel的規定[8]:“寬容地收,謹慎地發”。postel談的是網絡服務程式,但是其含義可以廣為适用。就算輸入的資料很不規範,一個 設計良好的程式也會盡量領會其中的意義,以盡量與别的程式協作;然後,要麼響亮地倒塌,要麼為工作鍊下一環的程式輸出一個嚴謹幹淨正确的資料。

然而,也請注意這條警告:

最初html文檔推薦“寬容地接受資料”,結果因為每一種浏覽器都隻接受規範中一個不同的超集,使我們一直倍感無奈。要寬容的應該是規範而不是它們的解釋工具。

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程式設計藝術》1.6 unix哲學基礎

繼續閱讀