The Art of unix programming
哲學(xué)
哲學(xué)基礎(chǔ):
unix 哲學(xué)并不算是一種正規(guī)設(shè)計(jì)方法, 不打算從計(jì)算機(jī)科學(xué)的理論高度產(chǎn)生出完美的軟件, 那些毫無動(dòng)力、松松垮垮的,薪水微薄的程序員,能在短短期限內(nèi),如同神靈附體般的創(chuàng)造出穩(wěn)定而又新穎的軟件, 這只不過是經(jīng)理人永遠(yuǎn)的夢(mèng)囈罷了。 (社會(huì)經(jīng)濟(jì)學(xué)基礎(chǔ))
unix 哲學(xué),是自下而上的,注重實(shí)效,立足于豐富的經(jīng)驗(yàn),你不會(huì)在正規(guī)的方法學(xué)和標(biāo)準(zhǔn)中找到她,她更接近與隱性的 半本能知識(shí),UNIX 文化所傳播的 專業(yè)經(jīng)驗(yàn)。
-
<<Notes on C programming>> 中表述的Unix哲學(xué):
- 你無法判斷程序會(huì)在什么地方耗費(fèi)運(yùn)行時(shí)間,瓶頸經(jīng)常會(huì)出現(xiàn)在想不到的地方,所以別急于胡亂找個(gè)地方改代碼,除非你已經(jīng)證實(shí)哪兒就是瓶頸所在。
- 估量,在沒有對(duì)代碼進(jìn)行估量,別去優(yōu)化速度。
- 花哨的算法在n很小的時(shí)候通常很慢,而n通常很小,花哨算法的常數(shù)復(fù)雜度很大,除非你確定n總是很大,否則不要使用花哨算法。
- 花哨算法相比于簡(jiǎn)單算法,通常更容易出bug,更難實(shí)現(xiàn),盡量使用簡(jiǎn)單的算法配合簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu)
- 數(shù)據(jù)壓倒一切,正確的算法不言自明,編程的核心是數(shù)據(jù)結(jié)構(gòu),而不是算法。
- 沒有原則 6
Ken Thompson 對(duì)第4點(diǎn)進(jìn)行了,強(qiáng)調(diào), 拿不準(zhǔn)就窮舉
-
Unix 本身的體現(xiàn):
- 模塊原則: 使用簡(jiǎn)潔的接口拼合簡(jiǎn)單的部件 計(jì)算機(jī)編程的本質(zhì)是控制復(fù)雜度。要編制復(fù)雜的軟件而又不至于一敗涂地的唯一方法就是降低其整體復(fù)雜度----- 用清晰的接口把若干簡(jiǎn)單的模塊組合成一個(gè)復(fù)雜軟件,如此一來,多數(shù)問題,只會(huì)局限于局部,而不是牽一發(fā)而動(dòng)全身。
- 清晰原則: 清晰勝于機(jī)巧
- 組合原則: 設(shè)計(jì)時(shí)考慮拼接組合
多數(shù)程序都盡可能采用過濾器的形式, 即將一個(gè)輸入的簡(jiǎn)單文本流處理為一個(gè)簡(jiǎn)單的文本流輸出. - 分離原則: 策略同機(jī)制分離,接口同引擎分離
- 簡(jiǎn)潔原則: 設(shè)計(jì)要簡(jiǎn)潔,復(fù)雜度能低就底
- 吝嗇原則: 除非確無它法, 不要編寫龐大的程序
- 透明性原則: 設(shè)計(jì)要可見, 以便于審核和測(cè)試
- 健壯原則: 健壯源于透明性與簡(jiǎn)潔
- 表示原則: 把知識(shí)深入到數(shù)據(jù)以求邏輯質(zhì)樸而健壯
數(shù)據(jù)要比編程邏輯更容易駕馭,在設(shè)計(jì)中,你應(yīng)該主動(dòng)的將代碼的復(fù)雜度轉(zhuǎn)移到數(shù)據(jù)中 - 通俗原則: 接口設(shè)計(jì)避免標(biāo)新立異
最少驚奇原則 - 緘默原則: 如果一個(gè)程序沒什么好說,就沉默
- 補(bǔ)救原則: 出現(xiàn)異常是,立馬退出并給出錯(cuò)誤信息
- 經(jīng)濟(jì)原則: 寧化機(jī)器一份,不花程序員一秒
- 生成原則: 避免手工hack, 盡量編寫程序生成代碼
- 優(yōu)化原則: 雕琢之前先得有原型,跑之前先學(xué)會(huì)走
過早優(yōu)化是萬惡之源, 先只做原型,在進(jìn)行細(xì)琢,優(yōu)化之前先確保能用。 - 多樣原則: 絕不相信所謂的不二法門,
- 擴(kuò)展原則: 設(shè)計(jì)招眼未來,未來總是比預(yù)想快
設(shè)計(jì)代碼時(shí)候, 需要很好的組織, 讓將來的開發(fā)者增加新功能是無需拆毀或重建整個(gè)架構(gòu),當(dāng)然這個(gè)原則并不是說你能隨意的增加根本用不上的功能。而是建議在編寫代碼時(shí)候考慮未來的需要,使以后增加功能比較容易,程序結(jié)合更靈活。
其實(shí)其中很不容易取舍。 - KISS keep it simple, stupid
- 如果不確定什么是對(duì)的,那么就只做最少的工作,確保任務(wù)完成就行,至少知道知道什么是對(duì)的。(善用工具,盡可能的將一切都自動(dòng)化)
模塊性: 保持清晰,保持簡(jiǎn)潔
代碼劃分,又一個(gè)自然的層次體系, 一開始,一切都是機(jī)器嗎,最早的過程語言引入了子程序來劃分代碼的概念,再后來發(fā)明了獨(dú)立地址空間和可以互相通訊的進(jìn)程,今天,習(xí)以為常的把程序系統(tǒng)分布到個(gè)個(gè)主機(jī)上。
要編寫復(fù)雜的軟件又不至于一敗涂地的唯一方法: 用定義清晰的接口把若干簡(jiǎn)單模塊組合起來,如此一來,多數(shù)問題只會(huì)出現(xiàn)在局部。
- 封裝和最佳模塊的大小: 模塊直接通過應(yīng)用程序編程接口(API) 來進(jìn)行通信。
- 實(shí)現(xiàn)層面API,阻止各自的內(nèi)部細(xì)節(jié)被其他模塊知曉。
- 設(shè)計(jì)層面, API,定義了真?zhèn)€系統(tǒng)(驗(yàn)證API是否定義良好,試著使用純?nèi)祟愓Z言來描述設(shè)計(jì),能夠把事情說清楚?先開始定義接口,進(jìn)行描述,然后開始實(shí)踐,使用代碼闡述設(shè)計(jì)。)
- 模塊過大或者過小都會(huì)造成更多的bug, 400-800 之間的代碼是最佳點(diǎn)。
- 緊湊性和正交性:
- 緊湊性: 一個(gè)設(shè)計(jì)是否能夠被人腦理解,需不需要操作手冊(cè),引導(dǎo)指南?(功能集合)
- 正交性: 有助于是復(fù)雜設(shè)計(jì)也能緊湊的最重要特性之一, 純粹的設(shè)計(jì)中,任何操作都應(yīng)該是沒有副作用的。(不知道為什么是使復(fù)雜設(shè)計(jì)緊湊的重要特性,而不是,將模塊解耦?所以模塊越緊湊越 少耦合)《程序員修煉之道》對(duì)其中有很深入的討論。
- SPOT原則(Single Point of Truth, DRY,Don't repeat yourself), 保持任何一個(gè)知識(shí)點(diǎn),在系通中是唯一的,明確的。
如果有大量的樣板式代碼,是不是可以用單一的高層次的表現(xiàn)形式來生成呢? - 自頂向下設(shè)計(jì)和自底向上設(shè)計(jì)
- 區(qū)分: 問問設(shè)計(jì)是否圍繞 主事件循環(huán) 組織的。還是圍繞著主循環(huán)可能調(diào)用的所有操的服務(wù)庫組織的。自頂向下的設(shè)計(jì)者通常先考慮程序的主事件循環(huán),然后才插入具體的事件。自底向上通??紤]封裝具體的任務(wù),然后再按照某種次序?qū)⑦@些操作粘合在一起。
- 應(yīng)該雙管齊下,自底向上中,能夠很好的封裝操作原語,從而易于變動(dòng),重用底層代碼,但是容易做與邏輯無關(guān)的工作。自頂向下:可能面臨應(yīng)用邏輯需要的跟真正能夠?qū)崿F(xiàn)的操作原語不同。從而讓然封裝底層的代碼操作。
- 雙管齊下的設(shè)計(jì)下:就需要膠合層的出現(xiàn),來緩解沖突。
- unix 和 面向?qū)ο?
- 面向?qū)ο缶幊讨?,作用于具體的數(shù)據(jù)結(jié)構(gòu)的函數(shù),和數(shù)據(jù)一起封裝在 可視為單元的一個(gè)對(duì)象中。非OO中,數(shù)據(jù)跟函數(shù)的聯(lián)系變的毫無規(guī)律。
- OO的設(shè)計(jì)理念在 圖形系統(tǒng),圖形用戶界面中得到非常多的認(rèn)可。然而,其他領(lǐng)域則很難發(fā)現(xiàn)有多少顯著的優(yōu)點(diǎn)。
- OO語言鼓勵(lì) 『復(fù)雜的抽象層次』, 在簡(jiǎn)單事情中,使事情變得很復(fù)雜。使程序員容易陷入過度分層的抽象中,過多的層次破壞了透明性。無在頭腦中理清楚代碼到底在干什么、如何運(yùn)行的。
好協(xié)議產(chǎn)生好實(shí)踐
透明性:來點(diǎn)光(Let There Be light)最有效的方法: 就是不要在具體操作的代碼上疊放太多的抽象層
需要思考的問題:
- 程序調(diào)用層次最大的靜態(tài)深度是多少?
- 代碼是否具有強(qiáng)大、明顯的不變性質(zhì)。不變性質(zhì)幫助人們推演代碼和發(fā)現(xiàn)有問題的情況
- 各個(gè)函數(shù)是否正交?是否有太多的特征標(biāo)記和模式位,使得一個(gè)調(diào)用要完成多個(gè)任務(wù),完全避免模式標(biāo)志會(huì)導(dǎo)致混亂, 里面包含太多的一模一樣的代碼,但是頻繁的使用模式標(biāo)志更容易產(chǎn)生錯(cuò)誤。(代碼中常見)
- 程序的數(shù)據(jù)結(jié)構(gòu)或者分類和他們所代表的外部實(shí)體之間,是否存在一對(duì)一的映射?(不必要,設(shè)計(jì)中不必一對(duì)一)
- 是否容易找到給定函數(shù)的部分代碼?不論單個(gè)模塊,函數(shù),需要花多少時(shí)間讀懂?
- 代碼是增加了特殊情況還是避免了特殊情況? 每一個(gè)特殊情況可能對(duì)任何其他特殊情況產(chǎn)生影響和隱含的沖突,特殊情況是的代碼更難以理解。
- 代碼簡(jiǎn)單最好,但是如果代碼很好的解決了以上問題,則代碼也可以復(fù)雜。而且不會(huì)對(duì)維護(hù)人員造成認(rèn)知負(fù)擔(dān)。
多道程序設(shè)計(jì): 分離進(jìn)程為獨(dú)立的功能
Unix 最具特點(diǎn)的程序模塊化技法: 就是將大型程序分解成為多個(gè)協(xié)作進(jìn)程。但是,幾乎沒有好的實(shí)踐方法。盡管將程序劃分成協(xié)作進(jìn)程帶來了全局復(fù)雜度的降低,蛋代價(jià)是我們必須更多的關(guān)注在進(jìn)程間傳遞消息和命令的協(xié)議設(shè)計(jì)。和 通信各方設(shè)計(jì)狀態(tài)機(jī)的問題。
-
Unix為多進(jìn)程提供的有:
- 降低多進(jìn)程 生成的開銷
- 提供方法、IO、管道、消息傳遞、套接字 簡(jiǎn)化進(jìn)程通信
- 提倡使用能有管道、套接字傳遞的簡(jiǎn)單、透明的文本數(shù)據(jù)格式
- 進(jìn)程: 為了降低復(fù)雜度,而非為了利用并發(fā)提升性能。線程: 為了提高性能,然而,線程并不是降低而是提高的全局復(fù)雜度。除非萬不得已,不要使用線程。
- Unix IPC 方法分類:
- shell調(diào)用其他程序,這種情形的要點(diǎn)在于, 專門程序在運(yùn)行時(shí)不需要跟父進(jìn)程交流,只要任意一方接受了 另外一個(gè)程序的輸入。
- 管道、重定向、過濾器: 管道線中的所有階段的程序是并發(fā)運(yùn)行的。注意到這一點(diǎn)很重要,每一段在等待前一段的輸出作為輸入。管道已經(jīng)成為老古董,被套接字取代。
- 從進(jìn)程
- 對(duì)等進(jìn)程間通訊: 通常意味著數(shù)據(jù)能夠自由的雙向流動(dòng)。
- 臨時(shí)文件: 文件名字沖突(shell腳本中的管理實(shí)在臨時(shí)文件命中包含'$$',這個(gè)shell變量將被展開為載入shell進(jìn)程的ID, 從而保證文件名的唯一性)
- 信號(hào): Unix信號(hào)是一種軟中斷形式,每個(gè)信號(hào)對(duì)接受進(jìn)程產(chǎn)生默認(rèn)作用(殺掉它),進(jìn)程可以聲明 信號(hào)處理程序,讓信號(hào)處理程序覆蓋默認(rèn)行為,信號(hào)最初被涉及到Unix中,最初是最為操作系統(tǒng)就某些錯(cuò)誤或關(guān)鍵事件通知程序的一種方式, 舉例來說, SIGHUP信號(hào),會(huì)在會(huì)話結(jié)束時(shí)被發(fā)送給每一個(gè)該指定終端會(huì)話啟動(dòng)的程序,例: 在用戶鍵入中斷(Ctrl-c)時(shí)候, 發(fā)送給當(dāng)前每一個(gè)連接鍵盤的程序,然而,信號(hào)經(jīng)常被用作,守護(hù)程序的控制通道,隨著信號(hào)IPC經(jīng)常使用的一種技法是所謂的pidfile, 需要信號(hào)的程序會(huì)向已知的位置寫入一個(gè)包含進(jìn)程ID的文件(/var/run)其他程序可以通過該文件來獲取PID, 如果守護(hù)程序只允許一個(gè)實(shí)例運(yùn)行,該pidfile, 也作為隱含的文件鎖使用。
- 套接字: 一種封裝網(wǎng)絡(luò)數(shù)據(jù)訪問的方法, 使用協(xié)議族來告訴網(wǎng)絡(luò)層如何解釋套接字的名稱, AF_UNIX作為同一臺(tái)機(jī)器上兩個(gè)進(jìn)程之間的通信方式.
- 共享內(nèi)存: 要求生產(chǎn)者和消費(fèi)者都必須在一個(gè)機(jī)器上,但是如果通信進(jìn)程能夠訪問同一塊物理內(nèi)存,則共享內(nèi)存為最快的信息傳遞方法。需要處理競(jìng)爭(zhēng)和死鎖問題.
- 要避免的問題和方法
- 廢棄的Unix IPC方法, System V IPC, Steams
- 遠(yuǎn)程過程調(diào)用(RPC), 最成功的RPC應(yīng)用,是網(wǎng)絡(luò)文件系統(tǒng),都是那些在應(yīng)用定義域上本來就只涉及很少量簡(jiǎn)單數(shù)據(jù)類型的應(yīng)用。支持RPC的理由通常是, 允許更豐富的接口,但是更不簡(jiǎn)介,(Unix 強(qiáng)烈贊成使用透明,可顯的接口, 這是Unix文化不斷堅(jiān)持文本協(xié)議的動(dòng)力)。RPC旺旺更多延遲的原因有: a: 無法準(zhǔn)確估算一個(gè)指令調(diào)用會(huì)涉及到多少數(shù)據(jù)的列表和散列,b:RPC模型鼓勵(lì)程序員忽視網(wǎng)絡(luò)交易成本,造成網(wǎng)絡(luò)時(shí)間消耗。
- 線程: 共享全局內(nèi)存,在這個(gè)共享地址空間管理競(jìng)爭(zhēng)和臨界區(qū)的任務(wù)相當(dāng)困難,增加整體復(fù)雜度。隨著鎖定機(jī)制復(fù)雜度的增加意外交互作用所造成的競(jìng)爭(zhēng)和死鎖機(jī)會(huì)也會(huì)增加。鎖定共享數(shù)據(jù)結(jié)構(gòu)以防止互相干涉的開銷非常昂貴,最關(guān)鍵的難題在于,各個(gè)系統(tǒng)中實(shí)現(xiàn)的標(biāo)準(zhǔn),沒有標(biāo)準(zhǔn),所以不可能進(jìn)行移植。
- 線程的使用應(yīng)該是最后一招而不是第一招,如果能夠使用優(yōu)先的共享內(nèi)存和信號(hào)量,使用SIGIO的一部I/O,poll,select 而不是使用線程,那就保持簡(jiǎn)單。
- 線程、遠(yuǎn)程過程調(diào)用、重量級(jí)的面向?qū)ο笤O(shè)計(jì)結(jié)合使用的時(shí)候,非常危險(xiǎn),如果你被邀請(qǐng)加入到使用這三者的項(xiàng)目中,逃之夭夭并不丟面子(快屁股走人,但是現(xiàn)在的似乎并不少,因?yàn)榇蠹覍?shí)在是現(xiàn)有的穩(wěn)定的軟件API,進(jìn)行隔離,并沒有自己寫bug ?_?)
- 編程是控制復(fù)雜度。能夠管理復(fù)雜度的工具是好工具,但是如果不是,扔掉!永遠(yuǎn)不要忘記這一點(diǎn),它是UNIX智慧的重要組成部分。
微型語言:
對(duì)軟件錯(cuò)誤模式進(jìn)行大量研究得出的一個(gè)一致的結(jié)論就是: 程序員每百行代碼出錯(cuò)率,和所使用的編程語言在很大程度上相關(guān)。更高級(jí)的語言可以使用更少的行數(shù)完成更多的任務(wù),也意味著更少的bug.
生成: 提升規(guī)格說明的層次
人類更善于肉眼觀察數(shù)據(jù),而不是推到控制流程。不同的方式在透明性和清晰性方面具有非常顯著的差別。數(shù)據(jù)比程序更容易駕馭。盡可能的把設(shè)計(jì)的復(fù)雜度從程序代碼中轉(zhuǎn)移到數(shù)據(jù)中是個(gè)很好的實(shí)踐。
- 數(shù)據(jù)驅(qū)動(dòng)編程
需要把代碼跟代碼作用的數(shù)據(jù)結(jié)構(gòu)劃分清楚。
跟面向?qū)ο髤^(qū)分是: 數(shù)據(jù)驅(qū)動(dòng)編程中,數(shù)據(jù)不僅僅是某個(gè)對(duì)象的狀態(tài),實(shí)際上還定義了程序的控制流程。 b:OO首先考慮的封裝,而數(shù)據(jù)驅(qū)動(dòng)編程看中的事編寫盡可能少的固定代碼。
配置: Starting on the Right foot
- 配置在哪里?
- /etc 下的運(yùn)行控制文件(配置文件)
- 有系統(tǒng)設(shè)定的環(huán)境變量
- 用戶主目錄下的控制文件
- 用戶設(shè)置的環(huán)境變量
- 啟動(dòng)程序命令行所傳遞的開關(guān)、參數(shù)
后面的會(huì)覆蓋前面的參數(shù)
考慮使用何種機(jī)制想程序傳遞配置數(shù)據(jù)時(shí)候, 要考慮參數(shù)的使用周期。
1. 對(duì)調(diào)用時(shí)可能發(fā)生變化項(xiàng): 使用命令開關(guān)。
2. 改動(dòng)很少但是確實(shí)應(yīng)該由每個(gè)用戶控制的項(xiàng): 用戶主目錄的運(yùn)行控制文件
3. 需要管理員控制,而不是用戶能夠改變的系統(tǒng)級(jí)項(xiàng): 系統(tǒng)的控制文件
- 運(yùn)行控制文件(配置文件的格式)
- 通常只在程序啟動(dòng)時(shí),一次性讀取,因此性能不是主要考慮的問題, 互用性和透明性要求我們使用文本格式。
- 特例: 如果程序是某種解釋器,那么應(yīng)該是用 該語言語言寫成,并在啟動(dòng)時(shí)候,執(zhí)行命令文件。(mina deploy.rb, 不知道靜態(tài)語言是不是會(huì)費(fèi)點(diǎn)勁?)
優(yōu)化: Optimization
關(guān)于性能優(yōu)化, Unix經(jīng)驗(yàn)告訴我們,最主要的就是如何知道合適不去優(yōu)化, 其次,最有效的優(yōu)化往往是優(yōu)化之外的其他事情,比如干凈的設(shè)計(jì)。
- 程序員工具箱中最強(qiáng)大的優(yōu)化技術(shù)就是不做優(yōu)化
- 先估量,后優(yōu)化。
復(fù)雜度: 盡可能簡(jiǎn)單,但是別簡(jiǎn)單的過了頭(As simple as possible, but Note Simpler)
| 偶然復(fù)雜 | 違反SPOT原則 | 過早優(yōu)化 | 非正交性 |
|---|---|---|---|
| 選擇復(fù)雜度 | 方法學(xué)開銷 | 別的一切 | 有效功能 |
| 本質(zhì)復(fù)雜度 | 開發(fā)工具 | 核心數(shù)據(jù)結(jié)構(gòu) | 功能需求 |
| 代碼庫規(guī)模 | 實(shí)現(xiàn)復(fù)雜度 | 接口復(fù)雜度 |
偶然復(fù)雜度經(jīng)常來源于
- 接口設(shè)計(jì)并非正交-- 即 沒有仔細(xì)的分解接口操作以使得每個(gè)操作只完成一件事情。
- 過早優(yōu)化
本質(zhì)接口復(fù)雜度: 通常無法去除,除非調(diào)整軟件的基本功能需求,代碼庫的本質(zhì)大小同選擇的開發(fā)工具有關(guān),其中最重要的因素可能就是實(shí)現(xiàn)語言的選擇。
復(fù)雜度的不同來源必須以不同的方法應(yīng)對(duì): 代碼庫規(guī)??梢圆捎酶玫墓ぞ邅斫鉀Q, 實(shí)現(xiàn)復(fù)雜度可以選擇更好的算法來處理,接口的復(fù)雜度必須著眼于更好的交互設(shè)計(jì)。
處理各種復(fù)雜度: 必然更依賴于見識(shí)而非方法,桶過發(fā)現(xiàn)更簡(jiǎn)單的方法,可以去除偶然復(fù)雜度,依賴上下文環(huán)境判斷哪些功能值得去做,可以去除選擇復(fù)雜度。而要去除本質(zhì)復(fù)雜度, 只能依賴玉對(duì)現(xiàn)實(shí)真諦的洞察和頓悟,從根本上重新定義索要解決的問題。
- 當(dāng)簡(jiǎn)潔不能勝任:
對(duì)于Unix堅(jiān)持簡(jiǎn)單的精神,常常伴隨著一種錯(cuò)誤的理解方式, Unix程序員常常認(rèn)為似乎所有的可能復(fù)雜度都是偶然復(fù)雜度, 更為甚者, 在Unix傳統(tǒng)中存在一個(gè)強(qiáng)烈的偏好,寧可去掉功能,也不能接受可能的復(fù)雜性。
計(jì)算資源以及人類的思考,同財(cái)富一樣,不是靠?jī)?chǔ)藏而是靠消費(fèi)來證明其價(jià)值的。同其他美學(xué)形勢(shì)一樣,我們需要主義何時(shí)設(shè)計(jì)上的簡(jiǎn)約已經(jīng)不在是有價(jià)值的自律形式,------而開始成為一件偽裝的苦行者外衣------ 實(shí)際上把美德作為接口來敷衍工作的縱容方式。
編輯器的適當(dāng)規(guī)模
所有真正有用的程序都想變成瑞士軍刀, Unix世界之外大型的成功商業(yè)整合應(yīng)用程序套件通常也證實(shí)了這一點(diǎn),而且直接挑戰(zhàn)了Unix的最簡(jiǎn)(基礎(chǔ))哲學(xué)。Zawinski
定律是正確的,他表明有些程序需要小巧,有些程序需要龐大,但中間的路是行不通的。vi編輯器的表面問題可以歸咎于歷史,然而更深次的問題應(yīng)該追溯到增加功能的壓力同vi信徒,添加新功能根本考慮不到對(duì)整體設(shè)計(jì)的復(fù)雜度影響。
Emacs和Wily的例子進(jìn)一步證明, 為什么有些程序需要做的如此龐大: 這樣幾個(gè)相關(guān)任務(wù)就可以共享環(huán)境。從實(shí)現(xiàn)者的角度,編輯和版本控制是獨(dú)立的,但是用戶更愿意有一個(gè)大的環(huán)境讓他們能夠指向文本部分,無需要在程序之間切來切去。
更普遍的, 讓我們假設(shè)整個(gè)Unix環(huán)境可以視作是社區(qū)的單一設(shè)計(jì)工作。那么『小巧銳利工具』的教義,降低接口復(fù)雜度和代碼庫規(guī)模的壓力,可能正好導(dǎo)向過手工陷阱-------- 用戶不得不自己維護(hù)所有共享的上下文環(huán)境,因?yàn)楣ぞ卟⒉粫?huì)替他們完成此項(xiàng)工作。
選擇 復(fù)雜度的價(jià)值依賴于對(duì)目標(biāo)的選擇。而在所有與任務(wù)相關(guān)的面相文本工具間共享上下文環(huán)境的能力是非常有價(jià)值的。Emacs 是個(gè)反傳統(tǒng)Unix的例子嗎?
Emacs 這樣龐大的程序會(huì)使Unix程序員極不舒服,恰恰因?yàn)樗麄儚?qiáng)迫我們面對(duì)這些沖突, 這些程序表明, 舊學(xué)派的Unix的簡(jiǎn)約主義作為一個(gè)原則雖然有其價(jià)值,蛋我們可能已經(jīng)陷入教條主義的錯(cuò)誤中。
- Unix風(fēng)格的小巧伶俐工具存在數(shù)據(jù)共享的困難, 除非他們生存在彼此之間通訊便利的框架中,Emacs就是這樣的一個(gè)框架,而對(duì)共享上下文環(huán)境的統(tǒng)一管理,正是其選擇復(fù)雜度換來的(共享上下文統(tǒng)一管理的實(shí)際效果就是用戶不需要負(fù)擔(dān)底層的命名和資源管理問題, 舊學(xué)派的unix中唯一的框架就是管道,重定向以及Shell,而共享上下文本質(zhì)上就是文件系統(tǒng)本身,但這并不是進(jìn)化的終點(diǎn)。)
最簡(jiǎn)原則暗示: 選擇需要管理的上下文環(huán)境,并按照邊界所允許的最小化方式架構(gòu)程序。 這就是『盡可能的簡(jiǎn)單,而不是過于簡(jiǎn)單』,集中關(guān)注選擇共享上下文環(huán)境,實(shí)際上,這并不僅僅適用于框架,也是用于應(yīng)用和程序系統(tǒng)。
然而, 究竟共享上下文環(huán)境該有多大實(shí)在很容易草率對(duì)待,隱藏在Zawinski定律后的壓力往往驅(qū)使應(yīng)用程序需要為便利性而共享上下文環(huán)境,很容易因?yàn)樨?fù)載太多任務(wù), 太多需求設(shè)想而最終失敗,也很容易就把程序編制的過于復(fù)雜、臃腫、龐大。
矯正這種趨勢(shì)的方法直接來源于舊學(xué)派Unix的贊美詩集。這就是吝嗇原則: 只有實(shí)證了其他方法行不通時(shí)才寫龐大的程序----也就是,已經(jīng)嘗試過分解問題但遭遇失敗。 格言表明對(duì)待龐大程序的一種嚴(yán)謹(jǐn)懷疑態(tài)度以及一種謹(jǐn)慎的做法: 首先尋找小巧程序的解決方案,如果單個(gè)小程序無法完成任務(wù),嘗試在現(xiàn)有框架結(jié)構(gòu)內(nèi)構(gòu)造一個(gè)協(xié)作小程序工具包來解決問題,如果兩者都失敗了,才可以自由的構(gòu)建一個(gè)巨型的程序,而不會(huì)覺得已經(jīng)完敗于設(shè)計(jì)。
但編制一個(gè)框架時(shí),牢記分離原則。框架實(shí)際值,盡可能少的包含策略。再多數(shù)情況中,根本就不需要策略,盡可能多的將行為分解到使用框架的模塊中。編制或重用框架的好處之一,是能夠有益于將『不這樣做會(huì)是大塊策略』的東西分離到獨(dú)立的模塊,模式或工具---- 可以有效的同其他程序重新組合起來的部分中去。
這些準(zhǔn)則是頗有價(jià)值和啟發(fā)性的方法,但是Unix傳統(tǒng)深處的這種矛盾沖突,并不能將任何給定的工程劃分為合理的最佳規(guī)模,并分而治之。具體情況具體分析,而鍛煉良好的判斷力和品位恰好是軟件設(shè)計(jì)者所追求的。行程才是目的,頓悟在每日的實(shí)踐中。
未來: 危機(jī)與機(jī)遇
Unix 設(shè)計(jì)中的問題:
- Unix文件就是一大袋字節(jié): 沒有其他的文件屬性, 特別是沒有能力存儲(chǔ)有關(guān)文件類型的信息。
- Unix對(duì)GUI的支持羸弱
- 文件刪除不可撤銷
- Unix假定文件系統(tǒng)是靜態(tài)的(假定程序的運(yùn)行總是暫時(shí)的,所以文件和目錄環(huán)境在整個(gè)執(zhí)行中都是可以作為靜態(tài)的,如果某個(gè)指定的文件或目錄發(fā)生了改變,沒有標(biāo)準(zhǔn)的、良好的方式讓系統(tǒng)通知應(yīng)用程序,當(dāng)編寫長(zhǎng)期運(yùn)行的用戶界面軟件時(shí)候,這將成為重大的問題。)
- Unix API并沒有異常,C語言缺乏拋出附帶數(shù)據(jù)的命名異常機(jī)制。