前言
Unix是開源運動歷史上最著名的代表之一汗贫,而如今前端是開源運動中最活躍的一支隊伍,我相信它們兩者之間一定有共通之處。讀懂此書,可以讓我們以后看待軟件開發(fā)時角度變得不那么狹隘阳掐。
內(nèi)容
前面三章精要的概括了Unix的哲學思想、歷史發(fā)展冷蚂、以及和其他操作系統(tǒng)的對比缭保。
第一章:哲學
第一章開門見山, 鞭辟入里地詮釋了Unix的靈魂所在:一門技術(shù)的編程藝術(shù)和設(shè)計哲學帝雇。
為什么它的生命力如此長久涮俄?作者列舉了兩個原因:一是伴生的C語言的龐大影響力,二是帶目錄節(jié)點的樹形文件名字空間和用于通信的管道機制尸闸。
支持者認為Unix有什么優(yōu)點呢?主要如下:
- 開源軟件孕锄,同僚復審
- 跨平臺可移植性和開放標準
- 與Internet的融合
- 開源社區(qū)
- 簡潔靈活
- 讓編程變得有趣
- 設(shè)計思想經(jīng)典吮廉,廣泛被借鑒
這些優(yōu)點同樣值得前端項目借鑒,大家想想jquery畸肆、vue宦芦、react、angular等是不是或多或少地符合以上幾點轴脐?
當然Unix也不全是優(yōu)點调卑,它在商業(yè)上的不成功和追隨者的過于狂熱成了反對者的詬病之處。
另外在設(shè)計理念上大咱,由機制而不是策略主導設(shè)計恬涧,認為最終用戶比設(shè)計人員更清楚自己想要什么也導致了一些問題。不過碴巾,時間證明溯捆,這才是保持生命力長久的秘訣。
至于具體優(yōu)點有哪些厦瓢,簡直是羅列不過來提揍,四個大前提,五個原則煮仇,十七點小的概況劳跃。不過總的來說就是開發(fā)人員需要分清輕重緩急、懷疑一切浙垫,并以幽默樂觀的態(tài)度面對一切刨仑。一言蔽之:Keep It Simple强重,Stupid。
本章的最后描述了這些設(shè)計哲學是如何應用在Unix中的贸人,我們前端同樣也可以應用间景,比如著名的前后端分離思想,協(xié)議文本化(http/json)等艺智。
- 依賴轉(zhuǎn)置
- 數(shù)據(jù)流文本化
- 數(shù)據(jù)庫和應用協(xié)議文本化
- 前后端分離
- 盡可能先寫原型
- 恰當混用編程語言
- 寬收嚴發(fā)
- 不需要丟的信息絕對不丟
- 小就是美
- 追求軟件設(shè)計的卓越化
第二章:歷史
Unix的發(fā)展倘要,作者使用了三重境界來描述,和王國維的“昨夜西風凋碧樹十拣。獨上高樓封拧,望盡天涯路。衣帶漸寬終不悔夭问,為伊消得人憔悴泽西。眾里尋他千百度,驀然回首缰趋,那人卻在捧杉,燈火闌珊處∶匮”三重境界頗為相似味抖,值得深思。
簡單評價一下Unix的歷史 :起于兼容分時系統(tǒng)灰粮,以簡單好用走遍江湖仔涩,廣納各高校貢獻,雖失勢于商業(yè)粘舟,終借Linux重獲新生熔脂,涅槃騰飛。
那推動Unix發(fā)展的英雄們都是誰呢柑肴?自然是hacker們霞揉,他們是計算機世界的江湖和俠客,雖然在社會和高校中各成一派嘉抒,不過在經(jīng)歷過互聯(lián)網(wǎng)大融合(Unix和TCP/IP零聚、ARPANET融合)和自由軟件運動后Unix文化已經(jīng)上升到一種意識形態(tài)的層面,協(xié)同開發(fā)也成為一種趨勢些侍,分權(quán)隶症、公開、同僚復審的特點在其中體現(xiàn)的淋漓盡致岗宣,最終導致了開源運動的興起蚂会。
老派Unix陣營在商業(yè)上是吃過虧的,而開源運動以一種更親和市場耗式、更少對抗性的方式把軟件介紹給外部世界胁住,從而也彌補了商業(yè)上的不足趁猴。
可以這樣說,軟件離開源越近越繁榮彪见,保持靈活性儡司,別和低價而靈活的方案較勁,就可以得到長久而旺盛的發(fā)展余指。
第三章:對比
這一章作者對比了Unix和其他操作系統(tǒng)的設(shè)計和編程習俗捕犬,雖然其中舉的很多例子已經(jīng)隨風而逝了,但是其核心理念的對比還是非常具有參考價值的酵镜。
首先碉碉,作者對操作系統(tǒng)的風格元素進行了對比, 因為它反映了操作系統(tǒng)設(shè)計者的意圖淮韭,體現(xiàn)了成本和編程環(huán)境的限制對設(shè)計的均衡影響垢粮,更重要的是這種文化會隨機漂移,影響其他軟件靠粪。
具體又從下面幾個角度進行詳細說明:
- 統(tǒng)一性理念
Unix一切皆文件以及管道概念蜡吧。
- 多任務能力
即多進程并發(fā)能力,Unix有搶占式多任務能力庇配。
其他操作系統(tǒng)有協(xié)作式多任務能力斩跌。
- 協(xié)作進程
Unix具有低價的進程生成成本、簡便的IPC捞慌、以及能組合各種管道過濾器小工具,這就避免了多線程的坑柬批,使系統(tǒng)的各個部分容易合作啸澡。
- 內(nèi)部邊界
以程序員最清楚一切作為準繩,從而采取多用戶控制權(quán)限,在系統(tǒng)內(nèi)部設(shè)置三層內(nèi)部邊界:內(nèi)存管理、多用戶權(quán)限組氮帐、涉及安全的可信代碼塊嗅虏。
- 文件屬性和記錄結(jié)構(gòu)
Unix沒有,因為覺得記錄結(jié)構(gòu)是一種雞肋的存在上沐。文件屬性可以幫助理解文件皮服,但在面向字節(jié)流工具和管道的世界中會有語義問題。
- 二進制文件格式
Unix采用文本格式,而二進制的兼容性和靈活性不好参咙。
7.首選界面風格
Unix采用CLI風格,而其他操作系統(tǒng)有GUI風格,雖然GUI或者CLI沒做好都有問題,但CLI更易于程序員得到自己想要的東西龄广。
- 目標受眾
Unix為技術(shù)用戶而設(shè)計,其他操作系統(tǒng)有為服務端、客戶端蕴侧、最終用戶择同、單機、聯(lián)網(wǎng)等受眾設(shè)計的净宵。
- 開發(fā)的門檻
開發(fā)門檻是由開發(fā)工具的金錢成本敲才、成為熟練開發(fā)者的時間成本裹纳、甚至文化門檻組成,Unix支持輕松編程、玩家文化和精英編程文化紧武。
- 具體操作系統(tǒng)比較
- VMS大全腫剃氧,商業(yè)領(lǐng)域尚可容忍
- MacOS的統(tǒng)一性理念是Mac界面方針,邊界脆弱阻星,MacOS X融合Unix特點
- OS/2單用戶系統(tǒng)朋鞍,不靈活
- Windows辣雞但勝在商業(yè)
- BeOS深入線程化疮绷、專攻大量數(shù)據(jù)處理涌萤,死在商業(yè)
- MVS死板,用于大型機
- VM/CMS是Unix祖先捍歪,在大型機領(lǐng)域如魚得水
- Linux繼承Unix思想矾踱,更進一步連接世界和人
最后作者總結(jié)恨狈,種瓜得瓜,種豆得豆呛讲,一個好的操作系統(tǒng)必須可移植禾怠、支持網(wǎng)絡(luò)、把握客戶端的力量贝搁。從這里我突發(fā)奇想吗氏,會不會操作系統(tǒng)的未來就是瀏覽器呢?
設(shè)計思想
從第四章到第十三章都是展開描述Unix各個設(shè)計思想的精髓雷逆,配有大量的例子弦讽,雖然這些例子大部分已經(jīng)湮沒在歷史的長河里,不過對當今的軟件開發(fā)仍然具有重要借鑒意義膀哲。
第四章:模塊性(保持清晰往产,保持簡潔)
隨著代碼的復雜度日益增加,我們需要使用子程序某宪、庫仿村、進程等來劃分代碼。比如可以用定義清晰的接口把若干簡單模塊組合起來兴喂,這樣把程序劃分開來蔼囊,并且讓它們易于協(xié)作,這是良好的設(shè)計方式衣迷。好的軟件滿足模塊化畏鼓、正交性、緊湊性三個特點蘑险。
而模塊化最突出的特點就是封裝滴肿。模塊間通過一組嚴密、定義良好的程序調(diào)用和數(shù)據(jù)結(jié)構(gòu)來進行通信佃迄。
有幾點需要注意:
- API是實現(xiàn)和設(shè)計間的滯塞點
- 簡潔清晰的API表明設(shè)計是良好的
- 模塊不宜過多也不宜多少
模塊化另一處特點是緊湊性和正交性泼差,一個軟件贵少,設(shè)計和人類使用習慣相符就是緊湊的,任何操作沒有副作用就是正交的堆缘。
但緊湊意味著精煉滔灶,實際上的軟件基本上是半緊湊型的,因為如果編程者需要記憶的條目數(shù)大于七說明API不是緊湊型的吼肥。比如C和Python是半緊湊的录平、C++是反緊湊的,造成這種現(xiàn)象的原因是有時候為了性能和適應范圍等要犧牲緊湊性缀皱。
正交性代碼在源頭上減少了bug的可能性斗这,更容易文檔化和復用,重構(gòu)根本的目標就是提高正交性啤斗。
實踐當中如何體現(xiàn)緊湊型和正交性呢表箭?作者告訴我們需要遵循SPOT原則、圍繞強單一中心钮莲、時刻考慮分離的價值免钻。
SPOT原則即真理的單點性,比如通過重構(gòu)去除重復代碼保留真理:
- 去除重復數(shù)據(jù)->讓代碼生成程序
- 去除重復代碼知識點->讓代碼生成部分文檔
- 去除重復頭文件和接口聲明->讓代碼生成頭文件和接口聲明
- 讓數(shù)據(jù)結(jié)構(gòu)“無垃圾崔拥,無混淆”
強單一中心即圍繞“解決一個定義明確的問題”設(shè)計程序极舔,例如diff算法、grep模式匹配链瓦、yacc生成語法解析器等都是專門為了解決某個問題才出現(xiàn)的拆魏。
分離的價值指從零開始,先去想盡量少的能做的事情慈俯,有點類似前端的漸進增強思想稽揭。
在進行模塊化之前,我們還需要考慮一個問題:那就是軟件的層次肥卡。
大體可以分為自頂向下和自底向上的層次,自頂向下適用于精確事镣、穩(wěn)定步鉴、底層自由的軟件,自底向上適用于探索性的編程璃哟,同時拋棄的代碼也比另外一種少氛琢。我們需要根據(jù)情況去設(shè)計,要么抽象化細節(jié)随闪,要么圍繞某個模型組織代碼阳似,而實際情況往往是結(jié)合兩種方式設(shè)計程序,這其中需要膠合層用來協(xié)調(diào)铐伴。
但膠合層要盡量薄撮奏,可以看做是分離原則的升華俏讹,C語言就是一個很好的膠合層。
膠合層的應用之一就是各個共享庫畜吊。雖然理論上膠合層不應存在泽疆,只有多變的策略和不變的機制。
另外作者討論了OO的思想玲献,他指出OO只在某些領(lǐng)域適用殉疼,Unix程序員們大多是持懷疑態(tài)度的,因為堆砌抽象層是很累人的事情捌年,容易陷入過度分層瓢娜,而且過早設(shè)計喪失了優(yōu)化的機會,以至于說:“過早優(yōu)化就是萬惡之源礼预∶呃”
這章的最后,作者設(shè)計了幾個問題供讀者自測逆瑞,看自己的代碼是否遵循了模塊化的原則:
- 全局變量有多少荠藤?
- 單個模塊的大小是否合適?
- 模塊內(nèi)的單個函數(shù)是否太大获高?
- 代碼是否有內(nèi)部API哈肖?
- API的入口是否超過七個?
- 模塊的入口點分布怎么樣念秧?
第五章:文本化(好協(xié)議產(chǎn)生好實踐)
上一章講了如何把程序拆分成若干個模塊淤井,這章就關(guān)于這些模塊間如何通信給出了答案。比如用于協(xié)作通信的應用協(xié)議如何設(shè)計摊趾,當然還有一種文本化的應用場景:數(shù)據(jù)存儲币狠,也會提到。
- 為了便于數(shù)據(jù)傳輸砾层,通常需要序列化(列集)和反序列化(散集)漩绵,比如前端中JSON的parse和stringify方法。
- 設(shè)計協(xié)議時需要考慮互用性肛炮、透明性止吐、可拓展性和經(jīng)濟性。
- 性能不需要最先考慮侨糟,不然可能會是一種過早優(yōu)化碍扔。
- 數(shù)據(jù)文件和控制文件的信息流方向是不一樣的。
文本化最重要的特點是透明秕重、可拓展不同。
比如Unix口令格式就是犧牲性能換透明和可拓展性的例子,.newsrc格式同樣是舍經(jīng)濟性而取透明性和可操作性。但也有適用于二進制的情景二拐,比如PNG格式服鹅。
隨后作者又道出了數(shù)據(jù)存儲文本化非常重要的一個特點:擁有一套句法和詞法約定的數(shù)據(jù)文件元格式。具體給了幾個例子卓鹿,JSON是我加上去的菱魔。
格式名 | 特點 |
---|---|
DSV(分隔符分隔值) | 使用冒號分隔值,通過反斜杠轉(zhuǎn)義符處理特殊情況吟孙,和CSV形成鮮明對比 |
RFC 822 | 使用Tab或者Space來延續(xù)澜倦,空行解釋為結(jié)束。應用于郵件和HTTP協(xié)議杰妓,有多個記錄的時候邊界可能不明顯 |
Cookie-Jar | fortune使用的格式藻治,使用%分隔,適用于結(jié)構(gòu)不易區(qū)別的文本段 |
Record-Jar | 前兩個格式的混合體巷挥,使用%%\n分隔桩卵,適用于可變字段的集合 |
XML | 類似于HTML、需要專門的解析器倍宾,適用于復雜的問題(比如遞歸嵌套) |
Windows INI | 使用名稱-屬性對分隔雏节,比較雞肋,復雜數(shù)據(jù)不及XML高职,簡單不如DSV |
JSON | 使用鍵值對分隔钩乍,如今比XML流行 |
那Unix對文本化格式的約定是什么樣的呢?
- 如果可能怔锌,以新行符結(jié)束的每一行只存一個記錄寥粹。
- 如果可能,每行不超過80個字符埃元。
- 使用#引入注釋涝涤。
- 支持反斜杠轉(zhuǎn)義約定。
- 在每行一條記錄的格式中岛杀,使用冒號或者任何連續(xù)的空白作為分隔符阔拳。
- 不要糾結(jié)tab和space。
- 優(yōu)先使用十六進制而不是八進制类嗤。
- 對于復雜的記錄衫生,使用“節(jié)”,如果有多行土浸,使用%%\n等作為分隔符。
- 節(jié)格式中彭羹,支持連續(xù)行黄伊。每行一個記錄字段或者用冒號終止字段名關(guān)鍵字作為引導字段。
- 格式可以自描述派殷,不然就設(shè)立一個版本號还最。
- 注意浮點數(shù)取整問題墓阀。
- 不要壓縮一部分。
前面花了這么多力氣描述數(shù)據(jù)存儲文本化拓轻,當然是給描述協(xié)議文本化做鋪墊斯撮。因為適用于數(shù)據(jù)文件格式的好處同樣也適用于協(xié)議。
協(xié)議設(shè)計另外需要注意的點就是要牢記端對端設(shè)計原則扶叉,包括安全勿锅、認證、性能枣氧。
同樣舉了例子溢十,關(guān)于郵件協(xié)議的:
- SMTP發(fā)郵件,設(shè)計良好达吞。
- POP3收郵件张弛,和SMTP如出一轍。
- IMAP收郵件酪劫,希望取代POP3吞鸭,但是加重了服務端的載荷減輕了客戶端的壓力。
最后話題一收覆糟,開始闡述應用協(xié)議元格式:文本格式刻剥、使用單行請求和響應,有效載荷數(shù)據(jù)多行搪桂,可以隨時拓展透敌。
需要解決的問題是簡化網(wǎng)絡(luò)間事務處理的序列化操作,因為網(wǎng)絡(luò)的帶寬昂貴的多踢械。比如HTTP酗电,非常簡單和通用,需要在安全和便利間做出抉擇内列,基于HTTP協(xié)議的其他協(xié)議很方便撵术,但是缺點也很明顯:完全客戶端驅(qū)動,要額外接收HTTP報警信息话瞧。
還有其他的例子嫩与,比如BEEP使用二進制包序列,且支持服務端推送信息交排,XML-RPC划滋、SOAP、Jabber埃篓,XML...雖然它們已經(jīng)隨著歷史逐漸沒落了处坪,但它們的優(yōu)點應該會被未來的HTTP2吸收,到時候給我們一個更強大的HTTP。
第六章:透明性(來點光)
優(yōu)雅軟件的第三點特點是什么呢同窘?可顯和透明性玄帕。
透明性指可以預測到程序行為的全部或者大部分情況,可顯性指幫助人們對軟件建立正確的“做什么想邦、怎樣做”的觀念裤纹。
可以從用戶(UI)和程序員(代碼)的角度來看待可顯性和透明性。
舉幾個例子:
實例名 | 特點 |
---|---|
audacity | 音頻軟件丧没,UI透明 |
fetchmail | 郵件軟件鹰椒,-v選項使得程序可顯,獲得“防彈程序”榮譽 |
GCC | 編譯器骂铁,一系列處理階段都是為了可顯而設(shè)計的吹零,例如dif |
kmail | 郵件程序,UI可顯透明拉庵,可以訪問具體細節(jié)但是又不顯眼 |
SNG | 圖形文本轉(zhuǎn)換軟件灿椅,可顯但不是很透明 |
Terminfo | 數(shù)據(jù)庫,可顯透明钞支,使用文件系統(tǒng)作為數(shù)據(jù)庫 |
Freeciv | 游戲茫蛹,數(shù)據(jù)文件以文本格式編寫 |
那怎樣設(shè)計軟件才能實現(xiàn)透明性和可顯性呢?這就需要我們專注代碼同其他人交流的方式了烁挟。
- 不要疊放太多抽象層婴洼,增加透明性。讓其他人能夠預測程序行為撼嗓。
- 編碼要求:調(diào)用深度要淺柬采、代碼要有明顯的不變性、API正交且警、全局設(shè)置唯一的記錄器粉捻、實體一對一映射、容易找到給定部分斑芜、避免增加特殊情況肩刃、避免意義含糊的常量。
- 避免過度保護杏头,對于錯誤調(diào)試應該是透明的盈包。
- 使用可編輯的表現(xiàn)形式:編寫文本化器或者瀏覽器。
- 便于故障診斷和故障恢復醇王,實現(xiàn)健壯性呢燥。
除此之外,還要為可維護性而設(shè)計寓娩,維護性就是其他開發(fā)者能夠順利地理解和修改軟件:
- 寧愿重構(gòu)疮茄,都要拋棄辣雞代碼滥朱。
- 努力讓代碼成為活代碼而不是睡代碼和死代碼,吸引別人來開發(fā)力试。
- 包含開發(fā)者手冊比用戶手冊更有效。
第七章:多道程序設(shè)計(分離進程為獨立的功能)
前面三章告訴我們軟件需要有模塊性排嫌、文本化畸裳、透明性、可顯性淳地、可維護性怖糊。這一章講述的部分更加接近實現(xiàn):如何將大型程序分解成多個協(xié)作進程,又稱多道程序設(shè)計颇象。
首先伍伤,我們需要遵循做單件事并做好的原則,提倡分解程序遣钳。
有三種方法:降低進程生成的開銷扰魂、簡化進程間通信、使用簡單透明的文本數(shù)據(jù)格式蕴茴。
但協(xié)議真正的設(shè)計挑戰(zhàn)是協(xié)議邏輯劝评,必須看得出很有表現(xiàn)力并可防范死鎖。
另外倦淀,不要過早關(guān)注性能蒋畜,我們需要從性能調(diào)整中分離復雜度控制,它們是兩碼事撞叽。于是作者強烈建議我們盡量不要使用線程姻成,還有模塊化可以達到更好的安全性。
接著作者介紹了Unix中的幾種IPC(進程間通信)方法愿棋,從最簡的逐步介紹到難的科展。
- 把任務轉(zhuǎn)給專門程序
最簡單的程序協(xié)作方法。專門程序運行時不需要和父進程進行交流初斑。
例如mutt郵件用戶代理辛润,將所有的編輯動作統(tǒng)一到單獨的emacs進程中。
- 管道见秤、重定向和過濾
各種IPC方法的誕生的源泉砂竖,管道約定每個程序一開始至少有兩個I/O數(shù)據(jù)流可用。
管道操作把一個程序的標準輸入連接到另一個程序的標準輸入鹃答,一系列管道被稱為管線乎澄,管道和重定向的組合威力很大,不需要單獨再寫一個完成某個功能的程序(類似鏈式調(diào)用)测摔,但它的缺點是單向性置济,不能讓數(shù)據(jù)雙向流動解恰。
實例是分頁程序的管道/制作單詞表、pic2graph浙于、bc(1)和dc(1)护盈、以及fetchmail為什么不用管線。
- 包裝器
它隱藏了shell管線的細節(jié)羞酗,被調(diào)用程序?qū)S没?br> 實例是備份腳本腐宋,只需要傳參調(diào)用就好。
- 安全性包裝器和Bernstein鏈
它是高級版包裝器檀轨,需要先認證再選擇性地調(diào)用胸竞。
實例是Bernstein鏈,每個繼發(fā)程序都取代了前一階段的程序参萄,并且可以在程序鏈中插入另一個程序來修改系統(tǒng)的行為
- 從進程
父子進程通信卫枝,只在兩種情況下使用:兩者使用的協(xié)議不重要或者那個協(xié)議是根據(jù)第五章原則設(shè)計的。(前端的mv*的父子組件通信也是這樣的)
實例是scp和ssh讹挎,scp從ssh中獲取信息制作進度條校赤。
- 對等進程間通信
對等通道,雙向數(shù)據(jù)流淤袜。(讓我想起了angular1.x的雙向數(shù)據(jù)綁定和其他前端框架的狀態(tài)管理)
種類 | 特點 |
---|---|
臨時文件 | 簡單痒谴,易沖突,有安全性風險 |
信號 | 一種軟中斷形式铡羡,信號間可能會競爭 |
系統(tǒng)守護程序和常規(guī)信號 | 殺進程信號和常規(guī)信號 |
套接字 | 從封裝網(wǎng)絡(luò)數(shù)據(jù)訪問的行為引申出來积蔚,需要指定協(xié)議族(前后端分開) |
共享內(nèi)存 | 最快,但是必須提供硬件烦周,自己處理競爭和死鎖 |
在使用這些IPC方法的時候尽爆,我們也需要注意一些問題:
- 不要讓IPC和API耦合在一起
- 盡量別使用二進制信息交換協(xié)議
- 避免混亂的流
- 避免遠程過程調(diào)用,它雖然會提升性能但延遲高
- 別用線程(線程恐嚇)读慎,因為作者認為它標準薄弱規(guī)范模糊
最后漱贱,我們在設(shè)計層次上的進程劃分無非就是完成程序在生命期內(nèi)交換數(shù)據(jù)的任務。
設(shè)計的過程中夭委,我們需要想清楚:
- 程序前后端分離幅狮,何時建立通信?
- 何時何地完成信息的列集和散集株灸?
- 何時產(chǎn)生緩沖問題崇摄?
- 怎樣保證獲取信息的原子性?
第八章:微型語言(尋找歌唱的樂符)
語言本身越精簡慌烧,出bug的可能性越低逐抑,當需要一個特定規(guī)則來完成任務時,微型語言就誕生了屹蚊。
它相對通用語言厕氨,體積小復雜度低进每。那什么情況下需要微型語言呢?當預先認識到設(shè)計一門微型語言命斧,注意到規(guī)格說明文件像微型語言田晚,或者老是打補丁的時候。
然后讓我們理解一下語言分類法国葬,由簡單到復雜肉瓦,是文件->微型語言->通用語言的。圖8.1很好地對當時主要語言進行了分類胃惜,它們的范疇從聲明型到命令型都有。
微型語言舉例
案例 | 特點 |
---|---|
sng | 圖文轉(zhuǎn)換哪雕,便于用通用工具編輯圖片 |
regexp | 描述文本模型船殉,簡單地表達了識別行為 |
glade | 界面創(chuàng)建,專門生成XML代碼 |
m4 | 宏處理斯嚎,便于把文本拓展成其他字符串利虫,但要慎用 |
XSLT | 描述文本流變換,具有有限的類型分類和對外接口 |
DWB | 公式排版堡僻,聲明性語義 |
fetchmail的運行控制語法 | 既有聲明性特點也有命令式特點糠惫,有語法糖 |
awk | shell語言,既不緊湊也不通用所以被廢棄 |
PostScript | 專門用于設(shè)備排版钉疫,堆棧式語言 |
bc和dc | 命令性語言硼讽,任意精度。dc是逆波蘭標記牲阁,bc是代數(shù)標記 |
Emacs Lisp | 前端語言固阁,可以描述編輯動作 |
JS | 客戶端語言宣肚,逐漸變得通用 |
設(shè)計微型語言
當需要把問題說明規(guī)格提升一個層次時锐秦,且應用領(lǐng)域的域原語簡單而固定不變時,我們就需要設(shè)計一門微型語言了顶考。
- 選擇正確的復雜度
盡量簡單凌唬,自頂向下設(shè)計并齐,符合美學。
了解嵌入型微型語言容易被濫用客税,宏的安全性差况褪。
把語言一樣的特性加進去作為事后補救措施是飲鴆止渴,一個偶爾圖靈完備的語言是其他開發(fā)者的噩夢霎挟。
- 擴展和嵌入語言
通過別的語言擴展或者嵌入進別的語言窝剖。這對實現(xiàn)命令式語言來說很好,但這個選擇決定于設(shè)計本身酥夭。
嵌入的話還需要考慮錯誤語法檢查赐纱。
- 編寫自定義語法
參考XML作為語法基礎(chǔ)脊奋。
功能簡單的微型語言就不要殺雞用牛刀了,遵循最小立異原則疙描。
確實需要自定義語法的話使用yacc和lex幫助诚隙。
- 慎用宏
宏帶來的問題大于好處,因為預處理器遇到表達式而不是期望的值的時候會發(fā)生意想不到的結(jié)果起胰。并且宏難以閱讀和調(diào)試久又,擾亂了錯誤診斷。
C預處理器有相應的解決辦法效五,但m4沒有地消。
- 考慮清楚設(shè)計的是微型語言還是協(xié)議
考慮語言引擎是否可被其他程序交互調(diào)用,關(guān)鍵在于事務邊界的標定程度畏妖。
第九章:生成(提升規(guī)格說明的層次)
這章的核心觀點就只有一個:把程序的邏輯轉(zhuǎn)移到數(shù)據(jù)中去脉执。也就是數(shù)據(jù)驅(qū)動編程,讓開發(fā)者只關(guān)心數(shù)據(jù)結(jié)構(gòu)而不是代碼戒劫。
數(shù)據(jù)驅(qū)動編程和OO的區(qū)別是數(shù)據(jù)實際上定義了程序的控制流半夷,OO是封裝和固定的。數(shù)據(jù)驅(qū)動和狀態(tài)機也是有區(qū)別的迅细,一個是自動生成代碼巫橄,一個是手工寫。
概括起來就是始終把問題層次往上推茵典。
案例 | 特點 |
---|---|
ascii | 不維護代碼湘换,只維護數(shù)據(jù) |
垃圾郵件統(tǒng)計 | 統(tǒng)計數(shù)據(jù)比精巧的模式匹配奏效 |
fetchmailconf | 通過配置文件生成代碼 |
所以我們需要代碼生成代碼。建設(shè)性的懶惰是大師級程序員的美德敬尺。
案例 | 特點 |
---|---|
生成ascii | 發(fā)布的源碼包含一個文件枚尼,里面是數(shù)據(jù),通過它們生成代碼 |
生成HTML | 數(shù)據(jù)+模板 |
第十章:配置(邁出正確的第一步)
配置是開發(fā)前啟動環(huán)境的重要步驟之一砂吞,另一個是交互通道署恍。
什么可以配置呢?理論上一切可配置蜻直,但配置項太多會爆炸盯质。(對新手不友好)
不如搞清楚什么不可配置:比如可以自動檢測的東西。用戶也不應該看到優(yōu)化開關(guān)概而,0.7秒以下藏起來呼巷。用其他程序能完成的任務就不要配置。
一般來說可以在五個地方配置:運行控制文件->系統(tǒng)環(huán)境變量->用戶運行控制文件->用戶自定義環(huán)境變量->命令行選項赎瑰。
這五個地方后面會覆蓋前面的王悍,而且越到后面變化的幾率越大,好的Unix實踐要求使用同參數(shù)選項預期壽命最匹配的機制餐曼。
- 運行控制文件
以rc結(jié)尾(比如eslintrc)压储,存放與程序相關(guān)的聲明或命令鲜漩,在程序啟動時解析。
它分系統(tǒng)的和用戶的集惋。語法有一套通用風格孕似,比如支持注釋、不區(qū)別空白符等刮刑『砑溃可以減少用戶閱讀和編輯時要接觸的新鮮事物,實例是.netrc文件:對用戶透明且遵循最小立異原則雷绢,但很難移植映射到其他操作系統(tǒng)里去泛烙。
- 環(huán)境變量
用來配置程序訪問的環(huán)境。如搜索路徑翘紊、系統(tǒng)默認值胶惰、uid、pid等關(guān)鍵信息霞溪。
也分系統(tǒng)的和用戶的。使用環(huán)境變量的時機是:變量值會根據(jù)上下文而變化或隨點文件不同而改變中捆、不能以改變命令行調(diào)用來表述鸯匹,操作系統(tǒng)間的移植同樣非常困難。
- 命令行選項
可以由腳本控制程序(如node腳本)泄伪,一般以-開頭殴蓬,有Unix(推薦)、GNU蟋滴、X toolkit風格染厅。
從-a到-z都被賦予了特殊的含義,某些大寫字母也是津函。盡一切辦法遵循最小立異原則和復用它們肖粮。
有命令行的地方就好移植。
那應該如何挑選方法呢尔苦?根據(jù)從運行控制文件->環(huán)境變量->命令行選項,是最不易改變到最易改變的原則挑選涩馆。
因為后者會覆蓋前者,并且依賴于程序在調(diào)用間隙需要保持多久的配置狀態(tài)允坚。
實例是fetchmail魂那,設(shè)置rc文件和環(huán)境變量,最后用命令行腳本化稠项。
但描述的約定不是絕對的涯雅,當明白自己想要什么并且想好了出錯后怎么補救,可以讓收益大于代價展运,是可以放手一搏的活逆。
第十一章:接口(Unix環(huán)境下的用戶接口設(shè)計模式)
接口是程序和程序精刷,程序和人類通訊的媒介。在設(shè)計時要遵循與其他程序通訊的前瞻性和最小立異原則划乖。
比如I/O有三種方式:程序贬养、IPC、已知文件或設(shè)備琴庵。所有存在的接口風格误算,存在即合理。
那如何應用最小立異原則來減少用戶學習負擔呢迷殿?
讓用戶對接口產(chǎn)生熟悉感儿礼,能共生和委派就弄,不能就效仿庆寺。
從Unix接口設(shè)計的歷史:打字機->命令行->可視化蚊夫,我們可以知道Unix接口鼓勵機制而非策略。
接口設(shè)計評估有五種度量標準:簡潔懦尝、表現(xiàn)力知纷、易用、透明和腳本化能力陵霉。
- 簡潔是指操作起來容易程度
- 表現(xiàn)力指接口可以表現(xiàn)出沒有預見到的行為組合
- 易用指學習成本低
- 透明說明用戶容易理解問題域
- 腳本化是指接口能被其他程序使用
在接口發(fā)展歷史上琅轧,CLI和GNU接口之間的爭論一直存在,它們分別面向?qū)<壹売脩艉统鯇W者用戶踊挠,但我們需要權(quán)衡看待乍桂。
CLI表現(xiàn)力強、簡潔效床、透明睹酌、腳本化但不易用,GNU表現(xiàn)力差剩檀、易用憋沿,其他不確定(看開發(fā)者)。
比如計算器程序是一個很好的GNU程序沪猴,因為它考慮到了用戶的未來行為卤妒,這是值得的。
不過Unix程序員的編程喜好還是透明字币、表現(xiàn)力和可配置则披,所以不用說,CLI是他們的最愛洗出。
Unix接口模式
模式 | 特點 |
---|---|
過濾器 | 標準輸入輸出士复,寬進嚴出,不丟不增,例如sort |
Cantrip | 一次性阱洪,例如clear |
源 | 只出便贵,例如ls |
接收器 | 只進,例如打印程序 |
編譯器 | 轉(zhuǎn)換信息冗荸,比如gcc |
ed | 有交互能力承璃,例如gdb |
Roguelike | 比GNU效率高,但難以腳本化蚌本,例如Roguelike游戲 |
引擎和接口分離 | 機制策略分離盔粹,遵循MVC模式,有配置者/行動者程癌、假脫機/守護進程舷嗡、驅(qū)動/引擎、C/S類型 |
CLI服務器 | 統(tǒng)一掌控程序啟動服務器進程嵌莉,例如CLI服務器 |
基于語言的接口 | 使用微型語言來做專門的事进萄,例如shell |
那我們非Unix程序員怎么應用Unix接口模式呢?
答案是促進腳本化和管道線能力锐峭,接口要盡量簡單中鼠。
首先交互要分三種情況:和初級用戶、專家用戶沿癞、其他程序使用兜蠕。
而且通常有幾種接口模式混合,首先封裝API邏輯抛寝,然后產(chǎn)生一個多價程序。
最重要的是cantrip曙旭、GUI盗舰、腳本接口模式,可選Roguelike模式桂躏。
然后作者闡述了網(wǎng)頁瀏覽器作為通用前端的好處钻趋,它統(tǒng)一了前端,讓前后端徹底分離剂习。
優(yōu)點有很多蛮位,比如CGI公用網(wǎng)關(guān)接口和ajax助力前后端通信。缺點是網(wǎng)頁強迫以批處理風格處理交互操作鳞绕、使用無狀態(tài)協(xié)議管理持久會話失仁。還有語言的兼容性成為一個問題(現(xiàn)在已經(jīng)不是問題,js統(tǒng)一了)们何。難以腳本化或?qū)⑹聞兆詣踊胶蠖耍喝龑蛹軜?gòu)萄焦,前端->CGI->命令腳本。
最后作者告誡我們接口沒什么可說的就閉嘴:遵循緘默原則。
因為無用信息會干擾合作拂封、用戶茬射、帶寬消耗,還有長時間的操作要提供進度條而不是廢話冒签。確認提示最好是“不”而不是“是”在抛。
調(diào)試模式和開發(fā)模式的消息可以區(qū)別對待。
第十二章:優(yōu)化
最有效的優(yōu)化是優(yōu)化之外的事情萧恕,例如清晰干凈的設(shè)計刚梭。
作者對于優(yōu)化這件事提出了自己的特有觀點:最好啥也別做,時間會給我們答案廊鸥。不寫代碼就沒有bug望浩,這點真是無力反駁。
摩爾定律告訴我們付出得不到回報惰说,軟件優(yōu)化的那一點點很快就被硬件升級所抵消磨德。要做也是做降維復雜度優(yōu)化而不是常數(shù)級的,比如從O(n^2)降到O(nlogn)
真要優(yōu)化的話吆视,先估量再優(yōu)化典挑,找到瓶頸再談優(yōu)化。
一般來說造成瓶頸有三個原因:
- 工具誤差啦吧,根本性的問題您觉。檢測工具的代碼執(zhí)行有誤差,可以統(tǒng)計它們的調(diào)用次數(shù)授滓。
- 外部延遲琳水,也是根本的,不能隨機檢測般堆,要多次檢測在孝。
- 過度調(diào)用,把子程序的時間開銷算到了調(diào)用程序中淮摔。
所以衡量性能時不要只收集孤立的性能數(shù)字私沮,更應該綜合多個參數(shù),如問題規(guī)模和橙、CPU速度仔燕、磁盤速度等,最后建模得出結(jié)論魔招。
還需要考慮非定域性(不確定性)之害晰搀,保持代碼短小簡單,核心數(shù)據(jù)結(jié)構(gòu)必須留在最快的緩存里可以盡量避免办斑。
某些優(yōu)化是不值得的厕隧,比如循環(huán)展開。
緩存越大,緩存的開銷越大吁讨,這點也需要注意髓迎。
本章后面一部分是針對協(xié)議優(yōu)化的內(nèi)容,關(guān)注點主要在吞吐量和延遲上建丧。
總的來說要設(shè)計出良好的網(wǎng)絡(luò)協(xié)議排龄,需要盡量避免協(xié)議的往返。
實際上盡可能使用低的時延設(shè)計和忽略帶寬成本翎朱。作者提供了三種策略減少時延:對事務批處理橄维、允許事務重疊、緩存拴曲。
1.批處理
先把更新累積起來争舞,最后一次性處理,比如DOM操作和DOMFragment。
2.重疊
將好幾條更新一起發(fā)送出去澈灼,阻塞和等待中間結(jié)果都是致命的,比如IMAP協(xié)議對請求做了標記竞川。
3.緩存
兼得魚和熊掌的策略,但必須考慮更新緩存的問題叁熔,更新模式越復雜委乌,bug越容易產(chǎn)生。
而且作者認為緩存對于SPOT原則來說是不好的荣回,因為它純粹為了性能優(yōu)化遭贸。他建議轉(zhuǎn)用加速文件系統(tǒng)或者虛擬內(nèi)存實現(xiàn)會比緩存好。所以我們在使用緩存時也應該問問自己為什么要用緩存心软。
第十三章:復雜度(盡可能簡單壕吹,但別簡單過了頭)
真實世界的編程就是管理復雜度的問題,我們應盡量降低復雜度删铃。
首先耳贬,我們需要理解復雜度是什么。作者分別從橫向縱向的角度進行了比較泳姐。
橫向的復雜度有三個來源:程序員、用戶暂吉、代碼胖秒。
- 程序員-接口復雜度,可能會陷進硬撐陷阱(極端晦澀的技法)慕的。
- 用戶-實現(xiàn)復雜度阎肝,可能會造成人力尺度陷阱(將許多底層任務拋給用戶)。
- 代碼-代碼量肮街,可能會陷入過專用陷阱(重復代碼)风题。
現(xiàn)實中可以對接口復雜度和實現(xiàn)復雜度折中,做出一方面是簡潔的接口,一方面是便于傳播的簡單軟件沛硅。
RG的文章認為MIT哲學(簡潔的接口)雖然讓軟件抽象地更好眼刃,但是New Jersey模型(簡單軟件)更具傳播特性,這兩種方法的平衡就在于可以拿此換彼摇肌,例如404的出現(xiàn)擂红。
縱向的復雜度有本質(zhì)的、選擇的和偶然的復雜度围小。
- 本質(zhì)的-有些問題天生就是復雜的昵骤,比如設(shè)計火箭程序。
- 選擇的-由工程目標決定肯适。
- 偶然的-沒有找到實現(xiàn)規(guī)定功能集合的最簡方法变秦。
我們必須要注意選擇和偶然復雜度的區(qū)別,偶然的可以由良好的設(shè)計去除框舔,選擇的只能改變工程目標了蹦玫。
映射復雜度
- 本質(zhì)接口復雜度通常無法去除,但可以調(diào)整代碼庫規(guī)模來減少代碼復雜度雨饺。
- 選擇復雜度邊界模糊钳垮,工程所涉及的任何方面都可能產(chǎn)生實現(xiàn)復雜度。
- 偶然復雜度可以通過良好的設(shè)計避免额港。
- 代碼復雜度可以采用更好的工具解決饺窿,實現(xiàn)復雜度可以選擇更好的算法解決,接口復雜度著眼于更好的交互設(shè)計移斩。
- 處理復雜度依賴于見識而非方法肚医。
有的時候由于本質(zhì)復雜度的存在,簡潔不能勝任向瓷,我們只能保證功能肠套,犧牲簡潔了。
為了讓我們對復雜度的理解更加深刻猖任,作者講了五個編輯器的故事你稚。(字處理器不在討論范圍內(nèi),因為過于專用)
種類 | 特點 |
---|---|
純文本 | 編輯器只知道其字節(jié)或者行結(jié)構(gòu) |
富文本 | 文本帶有屬性朱躺,如字體大小顏色等 |
句法感知 | 高亮刁赖,自動縮進 |
批命令輸出解析 | 可以編譯并捕捉錯誤 |
同輔助子進程交互 | 可以調(diào)試/版本控制/和其他程序通信 |
編輯器 | 特點 |
---|---|
ed | 純文本編輯 |
vi | 四不像 |
Sam | ed的進化版,新增了功能 |
Emacs | 大而全 |
Wily | 鼠標控制 |
由此作者總結(jié)了一下长搀,編輯器的適當規(guī)模應該是什么樣的宇弛。
首先甄別它們的復雜度:ed最簡單,Emacs最復雜源请,vi是折中派枪芒,Sam繼承了ed的簡潔彻况,Wily優(yōu)雅但有過于依賴鼠標的代價。接著批判了vi:折中無用舅踪。(雖然現(xiàn)在vi還是很火)
不過最后一句話說的還是好:少吃多干還是多吃多干取決于時代纽甘。(vscode應該是多吃多干,sublime應該是少吃多干)
結(jié)尾的時候引申到如何構(gòu)建軟件的適度規(guī)模:選擇需要管理的上下文環(huán)境硫朦,并且按照邊界所允許的最小化方式構(gòu)建程序贷腕。先證明其他方法行不通時再編寫大型程序。
具體實現(xiàn)
第十四章到第十六章介紹了Unix中涉及到的語言咬展、工具以及輪子泽裳,我們前端也需要考慮自身領(lǐng)域的相關(guān)問題,這對我們真正編碼的時候提升效率是非常有幫助的破婆。
第十四章:語言(C還是非C)
Unix下的語言是豐饒的涮总,并且鼓勵專門領(lǐng)域語言的設(shè)計。一方面祷舀,C語言是Unix的伴生語言瀑梗,另一方面,各種腳本語言在動態(tài)存儲管理的自動化上有巨大優(yōu)勢裳扯。
C和Unix的關(guān)系是巧妙的抛丽,沒有Unix就沒有C,沒有C就沒有如今Unix文化的繁榮饰豺,C和C++取代了匯編語言在工業(yè)界的地位亿鲜,重新掀起了一波技術(shù)浪潮。
雖然C和C++對要求極高的程序有意義冤吨,但損耗了程序員的精力蒿柳。
隨之而來急劇下降的成本又改變了編程的經(jīng)濟含義。軟件的復雜化說明自動化內(nèi)存管理越來越重要漩蟆,而且真正性能的損失往往來自外界垒探。(網(wǎng)絡(luò)延遲、事件等待等)
到當今這個年代怠李,混合策略才有可能使得效率最大化圾叼。即一種在主語言中嵌入其他語言的策略。
比如可以嵌入內(nèi)存管理器完成內(nèi)存管理捺癞,嵌入腳本膠合邏輯夷蚊。高級shell編程甚至可以自由混合語言編程。
接著作者對當時的主流語言進行了一番評估翘簇,因為熟悉語言才能更好地使用和組合它們撬码。
語言 | 特點 |
---|---|
C | 資源效率最接近機器語言儿倒,但資源管理非常困難 |
C++ | 效率高版保,支持OO和泛型編程呜笑,但非常難用,鼓勵過于復雜的設(shè)計 |
Shell | 完成小型任務自然快捷彻犁,但大型腳本必須依賴大量輔助命令造成兼容性問題 |
Perl | 強大的工具語言以及正則匹配叫胁,但大型項目不優(yōu)雅、難以維護 |
Tcl | 節(jié)儉緊湊的設(shè)計和作為解釋器語言的可拓展性汞幢,但數(shù)據(jù)結(jié)構(gòu)和命名空間等很怪異以至于難以用于大型項目 |
Python | 為嵌入而生的膠水語言驼鹅,代碼清晰優(yōu)雅,但效率不高 |
Java | 自動管理內(nèi)存并且支持OO森篷,但設(shè)計的有些復雜而且沒達到一次編寫處處運行的目的 |
Emacs Lisp | 結(jié)合了Lisp输钩,優(yōu)雅、自動管理內(nèi)存仲智,但難以移植买乃、性能差 |
作者還對這幾種語言的未來趨勢做了預測:C/C++/Java不變、Tcl/Perl衰退钓辆、Python增長剪验,事實證明他基本上是對的。
前面分析了那么多前联,該到自己動手選擇編程工具包的時候了功戚,因為GUI工具包是會影響編程狀態(tài)的,而且某些語言和工具包的綁定有特定要求似嗤,比如Qt屹立不倒啸臀,但我用vsc。
第十五章:工具(開發(fā)的技術(shù))
語言選好了双谆,工欲善其事必先利其器壳咕,接著就是選工具了。
首先顽馋,我們需要一個對開發(fā)者友好的操作系統(tǒng)谓厘,像Unix就沒有固定的IDE,需要自己組合工具完成IDE的功能寸谜。這樣可以讓程序員更加專注于設(shè)計竟稳,以編輯/編譯/調(diào)試為中心,其他細節(jié)用工具完成熊痴。前端也需要自己組合他爸。
- 編輯器
作者主要對比了vi和Emacs,但我認為vi類似于sublime果善,可以靈活拓展诊笤,Emacs類似于vsc宜咒,大而全抢韭,不過兩者兼用,用于不同的場景才是最佳策略。
- 專用代碼生成器
作者以lex和yacc這兩款生成語言詞法分析器的工具為例钳宪,介紹了lex是從輸入流中獲取標記符號离唬,而yacc是解析一系列標記符號來檢查是否符合語法啦桌。
但lex意外地被用于各種模式識別准颓,輸入一堆,最后找出某個模式凉馆。
工具生成的代碼還是比手工正確高效薪寓。
- 自動化編譯
把源碼進行裝配打包發(fā)布才是最重要的,以make為例
它會尋找代碼間的依賴關(guān)系從而生成正確的打包版本(類似webpack)澜共,但要注意不能非常復雜向叉,比如遞歸make。
一些腳本語言生成任務所需的文件也是很方便的嗦董,有all植康、test、clean展懈、install等命令销睁,類似npm。
makefile的可移植性和分析依賴能力靠幾個工具完成:makedepend存崖、Imake冻记、autoconf、automake等来惧。
- 版本控制系統(tǒng)
為了追蹤變化冗栗,特別是bug,查看作者供搀、時間隅居、內(nèi)容等,我們需要版本控制系統(tǒng)葛虐,而計算機更加擅長這些細節(jié)胎源。
手工版本控制隱性成本非常高,自動化的版本控制能夠保存項目的歷史評注并避免修改沖突屿脐。
舉例為VCS涕蚤,SVN是CVS的衍生版本(基于文件),GIT是現(xiàn)代版本控制系統(tǒng)(基于變化)的诵。
- 運行期調(diào)試工具
能夠打斷點万栅,檢查程序狀態(tài),可控執(zhí)行某個單一語句層次的部分西疤,這是透明性設(shè)計的另一幫手烦粒。
- 性能分析工具
程序90%的執(zhí)行時間都耗費在10%的代碼上,性能分析軟件幫助定位問題代赁,這樣就可以優(yōu)化關(guān)鍵的10%的代碼并遵循之前的優(yōu)化原則扰她。
- 整合工具
編輯/編譯/測試/調(diào)試/版本控制...一體的工具,對前端來說vsc+chrome可以完成95%的工作邮偎,不是IDE勝似IDE。
第十六章:重用(論不要重新發(fā)明輪子)
無為代碼义黎,天下希及。這個無為是指最經(jīng)濟的行為豁跑,對無論是人員資本還是經(jīng)濟收益都有好處廉涕。
而讓代碼無為就是重用代碼,重用代碼又是避免發(fā)明輪子的最有效方法艇拍。Unix里里外外都支持重用狐蜕,組合優(yōu)先于獨立。
作者還特意講了一個豬小兵的故事卸夕,這是千千萬萬程序員的縮影:
我們在工作中重用的代碼可能會有問題层释,不得逼我們重造一個輪子。但重用代碼是技術(shù)問題快集、知識產(chǎn)權(quán)壁壘贡羔、行政問題以及個人自我意識的綜合,所以代碼專用化還是開放化讓程序員們糾結(jié)个初。
不過乖寒,決定重用了,就必須透明院溺。比如用源碼和注釋幫助使用者理解代碼楣嘁,牢記只有變化才是永恒的,源碼可以延續(xù)而二進制碼不行珍逸。
那重用和開源的關(guān)系又是什么樣呢逐虚?作者說開源和重用就像愛情和繁殖的關(guān)系一樣,開源也是為了重用自然而然發(fā)展而成保護透明性優(yōu)勢的策略谆膳。對開發(fā)者來說叭爱,保證了經(jīng)驗的價值,這也是職業(yè)發(fā)展的動力漱病。
開放源碼是從意識形態(tài)上解決這些所有問題的優(yōu)先方法涤伐。
另外一點是,開源質(zhì)量通常大于閉源缨称。因為同行復議保證了標準凝果,評估開源代碼的方法是閱讀其文檔和它的部分代碼,如果有一定年頭睦尽、反饋器净、協(xié)同作者數(shù)、社區(qū)当凡,這份代碼就是質(zhì)量高的山害。
去哪找呢纠俭?代碼庫和專用開源網(wǎng)站。作者推薦了SourceForge浪慌、Freshmeat等網(wǎng)站冤荆,現(xiàn)在應該是Github。
找到重用代碼就是節(jié)約自己的編碼時間权纤,閱讀代碼的元數(shù)據(jù)并且試一試對自己是有好處的钓简,并且閱讀代碼的細節(jié)也是為未來投資。
但使用開源軟件還需要注意幾個問題:考慮質(zhì)量汹想、文檔外邓、許可證。
文檔的話古掏,專用文檔不如How To&FAQ等搜索來理解的快损话。
許可證相關(guān)的我們需要知道版權(quán)和許可證是兩碼事,誰是版權(quán)所有者不重要槽唾,關(guān)鍵是許可證條款丧枪。它讓我們使用、修改代碼的權(quán)利有限制庞萍,標準許可證有MIT豪诲、BSD等,GPL帶有病毒性質(zhì)挂绰,LGPL和MPL則削弱了這一點屎篱。另外記得,找律師只有1%的幫助...
社區(qū)的力量
最后幾章揭示了Unix為何生命力如此長久的原因葵蒂,在人和技術(shù)的平衡關(guān)系上做了非常仔細而微妙的分析交播。
第十七章:軟件可移植性與遵循標準
軟件開源了,你想讓更多的人使用你的軟件践付,但是傳播的障礙常常來自操作系統(tǒng)和硬件結(jié)構(gòu)秦士。
移植性一直是Unix的主要優(yōu)勢,所以一旦設(shè)想軟件項目生命周期很短永高,就容易犯錯隧土。
只要在架構(gòu)、接口和實現(xiàn)上命爬,API是穩(wěn)定的曹傀,其他特殊細節(jié)都是無關(guān)緊要的。
比如饲宛,C和Unix緊密關(guān)聯(lián)皆愉,是硬件和操作系統(tǒng)間的薄膠合層。它是在1971年誕生的,后期逐步引入typedef幕庐、union等操作符久锥,版本7引入了枚舉,并且將結(jié)構(gòu)體和union作為一等公民异剥。C語言標準造成了“K&R C”和“ANSIC”的區(qū)別瑟由,并且產(chǎn)生了一個很好的實踐:在標準化之前,先實現(xiàn)各種pollify冤寿。
再延伸到Unix標準歹苦,同樣使用公開標準作為API說明。雖然經(jīng)過了分裂和內(nèi)戰(zhàn)疚沐,但Unix的標準在實踐中得以奠定下來。
開源社區(qū)為了標準化潮模,也需要確保源碼的兼容性很強亮蛔。
舉個例子:IETF和RFC標準化過程,里面就體現(xiàn)了互聯(lián)網(wǎng)工程任務組的思維方式:標準必須來自于一個可用原型實現(xiàn)的經(jīng)驗擎厢。
當然也有理想化的標準究流,比如臭名昭著的七層OSI模型。我們要考慮這點:在成為標準之前动遭,實現(xiàn)的要求是越來越高的芬探。所以只有當草案標準經(jīng)過了實現(xiàn)的廣泛測試并且達到了普遍接受的程度,就真正成為標準了厘惦。
對此作者打了個形象的比方:規(guī)格是DNA偷仿,代碼是RNA。因為代碼是可棄的宵蕉,標準才是應該保留完善的酝静。
代碼從屬于標準,先做一個原型再不斷地測試和演進才是好辦法羡玛,生成半自動化的測試套件也是一個主要優(yōu)勢别智,可以穩(wěn)步迭代。至于相關(guān)的系統(tǒng)行為爭論可以在規(guī)則功能層面解決稼稿,非規(guī)格(功能)即bug薄榛。
話題順著到可移植性編程上,這個問題看似是準空間問題让歼,實際上時間上的持久性同樣重要敞恋。
首要問題是選擇語言,作者對當時流行的語言做了移植性分析:
語言 | 移植性特點 |
---|---|
C | 高谋右,但對于IPC耳舅、線程和GUI接口有困難 |
C++ | 類似C |
Shell | 差,大部分shell使用了其他可移植性差的工具 |
Perl | 良,看情況 |
Python | 優(yōu)秀 |
Tcl | 一般浦徊,隨項目復雜度有差異(看依賴) |
Java | 出色馏予,但幾個版本間的GUI有兼容問題 |
Emacs Lisp | 相當好,問題出在使用C接口的地方 |
總的來說就是避免系統(tǒng)依賴性盔性,發(fā)布源碼勝過二進制碼霞丧,不要想著幫助不大的移植工具。
所以現(xiàn)在js成了可移植性語言之王冕香。JavaScript is everywhere蛹尝。
另外一點和移植化有關(guān)的是國際化,實現(xiàn)它我們需要分離信息庫和代碼悉尾,并且盡量使用UTF8字符集突那,使用正則時注意字符范圍就好了。
那可移植性/開放標準和開放源碼有什么關(guān)系呢构眯?可移植性需要標準愕难,而開源促進了標準化。另外惫霸,不要依賴專有技術(shù)猫缭,哪天作者跳坑就GG。
第十八章:文檔(向網(wǎng)絡(luò)世界闡釋代碼)
Unix最初的目的就是整理文檔壹店,troff格式器是始祖猜丹,現(xiàn)在的趨勢是朝著html和url鏈接發(fā)展。
首先讓我們區(qū)分一下標記型和可視型的文檔:一種是面向程序員的硅卢,一種是面向初級用戶的射窒。
標記型又分表現(xiàn)型和結(jié)構(gòu)型的,而大多數(shù)以標記為中心的文檔系統(tǒng)都支持宏将塑。
Unix風格的文檔具備幾個文化和技術(shù)特征:
- 偏愛大文檔
- 寫給技術(shù)人員看轮洋,手冊頁往往包含一個BUGS部分
- 擅長編寫參考書籍
各種Unix文檔格式
文檔類型 | 特征 |
---|---|
troff和DWT | 表示層語言不如結(jié)構(gòu)層語言,大量用于技術(shù)文檔 |
TEX | 使用輔助程序比如LETEX編寫抬旺,大量用于數(shù)學和科學領(lǐng)域 |
Texinfo | 可以生成HTML |
POD | Perl的標記系統(tǒng)弊予,可以生成手冊但不能生成HTML |
HTML | 未來趨勢,在生成索引上有問題 |
DocBook | XML文檔類型定義开财,可以轉(zhuǎn)換成HTML汉柒、PDF等格式 |
于是書中大膽的預言未來的出路是XML一統(tǒng)天下...然而現(xiàn)在json橫空出世...
對于DocBook,作者還特意描述了一下:有一條轉(zhuǎn)換工具鏈责鳍,先驗證是否是符合正確的文檔格式碾褂,再根據(jù)樣式單加樣式最后輸出。
但是最后還是批判了這條又臭又長的工具鏈历葛,即使優(yōu)化成FOP了還是不咋地正塌。
最后本章總結(jié)了編寫文檔的最佳實踐:就是不要忽悠讀者嘀略。
- 數(shù)量多不會被認為質(zhì)量高
- 信息密度要適中
- 大項目最好發(fā)布手冊頁/教程/常見問題解答列表
- 文檔中要有readme
- 考慮新手用戶,技術(shù)名詞盡量用全稱
- 文檔格式應該易于傳播
第十九章:開源(在社區(qū)中編程)
Unix在開放源碼上就做了很好的表率乓诽,將找/改bug的任務分解成多個并行的子任務帜羊,然后眾力編程。
而開源有如下幾個特點:
- 源碼公開
- 盡早發(fā)布/經(jīng)常發(fā)布鸠天,前提是項目正常運行
- 給貢獻給予表揚
- 開源項目管理盡量自動化
之后便是本章的重點:如何與開源開發(fā)者協(xié)同工作的最佳實踐
- 良好的修補實踐
1.1 是否換位思考/知道合并的后果
1.2 發(fā)送dif部分/針對當前版本/不要包含可生成文件/不要發(fā)送系統(tǒng)自動拓展的字段
1.3 在補丁中包含文檔/解釋/有用的注釋
通過代碼質(zhì)量評估補丁
良好的命名實踐
2.1 使用GNU風格讼育,例如foobar-1.2.3.tar。gz
2.2 文件名/版本和區(qū)分度是最重要的
2.3 尊重適當?shù)谋镜丶s定
2.4 選擇容易鍵入的前綴良好的開發(fā)實踐
3.1 不要依賴專有代碼
3.2 使用GNU自動工具管理項目
3.3 先測試再發(fā)布代碼
3.4 發(fā)布前對代碼進行健全檢查(能夠捕捉到錯誤)
3.5 對readme進行拼寫檢查
3.6 考慮移植性良好的發(fā)布制作實踐
4.1 確保打包文件總是解包到單一新目錄下
4.2 包含README文件(項目介紹稠集、項目demo演示奶段、環(huán)境問題、關(guān)鍵架構(gòu)剥纷、編譯安裝指令痹籍、維護者光榮榜、項目新聞晦鞋、項目郵件列表地址等)
4.3 尊重和遵從標準文件命名實踐(看社區(qū)的習俗)
4.4 為可升級性設(shè)計
4.5 提供RPM(類似npm)
4.6 提供校驗和
供他人更好地下載蹲缠、獲取和使用
- 良好的交流實踐
5.1 在社區(qū)和社交平臺發(fā)公告
5.2 建立一個網(wǎng)站
5.3 提供項目郵件列表
5.4 發(fā)布到主要的檔案站點
便于招攬用戶與合作者
本章最后分析了許可證如何挑選,畢竟它會對軟件施加限制鳖宾。
雖然可以直接放在公共域吼砂,但使用某個標準許可證可以避免很多爭論逆航。
有MIT鼎文、BSD、GPL等許可證因俐,具體可以看阮一峰的如何選擇開源許可證
終章:危機與機遇
本書的結(jié)尾章拇惋,總結(jié)了過去如何應對的設(shè)計挑戰(zhàn),以及未來確定需要解決的問題和有待開拓的機會抹剩。
Unix最終要的是什么撑帖?當然是它的文化,而從傳統(tǒng)來看它有平質(zhì)和偶然屬性澳眷,平質(zhì)屬性和偶然屬性是可以互相轉(zhuǎn)化的胡嘿。
在歷史的長河中,三個特殊的技術(shù)變化驅(qū)動了Unix設(shè)計風格中的重大變革:網(wǎng)絡(luò)互聯(lián)钳踊、位圖圖形顯示和PC普及衷敌。
在這其中Unix一直保持著獨有的設(shè)計準則:模塊化、透明性拓瞪、機制同策略分離等缴罗。
有人嘗試重做Unix(Plan9),但最終失敗祭埂,不過給予了Unix發(fā)展的啟迪面氓。這是一個比Unix更Unix的設(shè)計,并且還增加了一個概念:私有命名空間,但更優(yōu)秀解決方案的最危險敵人舌界,就是一個現(xiàn)存的掘譬、足夠優(yōu)秀的代碼庫,沒有質(zhì)變禀横,誰會改變自己的慣性使用新事物呢屁药?
當然,Unix設(shè)計中也存在許多問題柏锄,這里著重討論幾個存在爭論的失敗之處:
- Unix文件只有字節(jié)
- Unix對GUI的支持孱弱
- 文件刪除不可撤銷
- 假定文件系統(tǒng)是靜態(tài)的
- 作業(yè)控制設(shè)計拙劣
- API的異常處理不好
- 設(shè)備中插入鉤子的方法(ioctl和fcntl)是個雞肋
- 安全模型太過原始
- 名字種類太多
- 文件系統(tǒng)的爭論
- 朝向全局互聯(lián)網(wǎng)地址空間
跳出程序員的眼界酿箭,來看看整個社會環(huán)境下,Unix如何發(fā)展:
首先要獲得持續(xù)的經(jīng)濟支持趾娃,提高程序員的社會價值缭嫡,然后組織終端用戶測試,獲取良好的反饋抬闷,最后要反對微軟/好萊塢等巨頭妇蛀,為自由而斗爭。
Unix文化中也有問題:內(nèi)部轉(zhuǎn)型的小問題和克服歷史上的優(yōu)越感的大問題笤成。
比如和Mac之爭评架,但Mac和Unix的設(shè)計哲學都有正確的一面,應該互相理解炕泳。不要把自己從騎士變成惡龍纵诞。
舍得拋棄過去,不再過分依賴那些已經(jīng)很好地為我們工作過的設(shè)想培遵。
勝利也不是全面的浙芙,低端市場和非技術(shù)用戶被忽略了。
最后的最后籽腕,作者語重心長的說:
“我們能贏嗡呼,只要我們想贏』屎模“