莊周夢蝶

          生活、程序、未來
             :: 首頁 ::  ::  :: 聚合  :: 管理

          《Unix編程藝術》重讀筆記(二)

          Posted on 2010-02-19 19:25 dennis 閱讀(3403) 評論(1)  編輯  收藏 所屬分類: 模式與架構 、涂鴉 、計算機科學與基礎

          模塊性:保持清晰,保持簡潔

          軟件設計有兩種方式:一種是設計得極為簡潔,沒有看得到的缺陷;另一種是設計得極為復雜,有缺陷也看不出來。第一種方式的難度要大得多。

          模塊化代碼的首要特質就是封裝API在模塊間扮演雙重角色,實現層面作為模塊之間的滯塞點阻止各自的內部細節被相鄰模塊知曉;在設計層面,正是API真正定義了整個體系。

          養成在編碼前為API編寫一段非正式書面描述的習慣,是一個非常好的方法。

          模塊的最佳大小,邏輯行200-400行,物理行在400-800之間。

          緊湊性就是一個設計能否裝入人腦中的特性。測試軟件緊湊性的一個簡單方法是:一個有經驗的用戶通常需要用戶手冊嗎?如果不需要,那么這個設計是緊湊的。

          理解緊湊性可以從它的“反面”來理解,緊湊性不等于“薄弱”,如果一個設計構建在易于理解且利于組合的抽象概念上,則 這個系統能在具有非常強大、靈活的功能的同時保持緊湊。緊湊也不等同于“容易學習”:對于某些緊湊 設計而言,在掌握其精妙的內在基礎概念模型之前,要理解這個設計相當困難;但一旦理解了這個概念模型,整個視角就會改變,緊湊的奧妙也就十分簡單了。緊湊也不意味著“小巧”。即使一個設計良好的系統,對有經驗的用戶來說沒什么特異之處、“一眼”就能看懂,但仍然可能包含很多部分。

          評測一個API緊湊性的經驗法則是:API入口點通常在7左右,或者按《代碼大全2》的說法,7+27-2的范圍內。

          重構技術中的很多壞味道,特別是重復代碼,是違反正交性的明顯例子,“重構的原則性目標就是提高正交性”。

          DRY原則,或者稱為SPOT原則(single point of truth)——真理的單點性。重復的不僅僅是代碼,還包括數據結構,數據結構模型應該最小化,提倡尋找一種數據結構,使得模型中的狀態跟真實世界系統的狀態能夠一一對應。

          要提高設計的緊湊性,有一個精妙但強大的方法,就是圍繞“解決一個定義明確的問題”的強核心算法組織設計,避免臆斷和捏造,將任務的核心形式化,建立明確的模型。


          文本化:好協議產生好實踐

          文本流是非常有用的通用格式,無需專門工具就可以很容易地讀寫和編輯文本流,這些格式是透明的。如果擔心性能問題,就在協議層面之上或之下壓縮文本協議流,最終產生的設計會比二進制協議更干凈,性能可能更好。使用二進制協議的唯一正當理由是:如果要處理大批量的數據,因而確實關注能否在介質上獲得最大位密度,或者是非常關心將數據轉化為芯片核心結構所必須的時間或指令開銷。

          數據文件元格式:

          1DSV風格DElimiter-Seperated Values

          使用分隔符來分隔值,例如/etc/passwd

          適合場景:數據為列表,名稱(首個字段)為關鍵字,而且記錄通常很短(小于80個字符)

          2RFC 822格式

          互聯網電子郵件信息采用的文本格式,使用屬性名+冒號+值的形式,記錄屬性每行存放一個,如HTTP 1.1協議。

          適合場景:任何帶屬性的或者與電子郵件類似的信息,非常適合具有不同字段集合而字段中數據層次又扁平的記錄。

          3Cookie-jar格式。簡單使用跟隨%%的新行符(或者有時只有一個%)作為記錄分隔符,很適用于記錄非結構化文本的情況。

          適合場景:詞以上結構沒有自然順序,而且結構不易區別的文本段,或適用于搜索關鍵字而不是文本上下文的文本段。

          4Record-jar格式cookie-jarRFC-822的結合,形如

          name:dennis
          age:
          21
          %%
          name:catty
          age:
          22
          %%
          name:green
          age:
          10

          這樣的格式。

          適合場景:那些類似DSV文件,但又有可變字段數據而且可能伴隨無結構文本的字段屬性關系集合。

          5、XML格式,適合復雜遞歸和嵌套數據結構的格式,并且經??梢栽跓o需知道數據語義的情況下僅通過語法檢查就能發現形式不良損壞或錯誤生成的數據。缺點在于無法跟傳統unix工具協作。

          6Windows INI格式,形如

          [DEFAULT]
          account
          =esr

          [python]
          directory
          =/home/ers/cvs/python
          developer
          =1

          [sng]
          directory
          =/home/esr/WWW/sng
          numeric_id
          =1001
          developer
          =1

          [fetchmail]
          numeric_id
          =4928492

          這樣的格式

          適合場景:數據圍繞指定的記錄或部分能夠自然分成“名稱-屬性對”兩層組織結構。

          Unix文本文件格式的約定:

          1、如果可能,以新行符結束的每一行只存一個記錄

          2、如果可能,每行不超過80字符

          3、使用”“引入注釋

          4、支持反斜杠約定

          5、在每行一條記錄的格式中,使用冒號或連續的空白作為字段分隔符。

          6、不要過分區分tabwhitespace

          7、優先使用十六進制而不是八進制。

          8、對于復雜的記錄,使用“節(stanza)”格式,要么讓記錄格式和RFC 822電子郵件頭類似,用冒號終止的字段名關鍵字作為引導字段。

          9、在節格式中,支持連續行,多個邏輯行折疊成一個物理行

          10、要么包含一個版本號,要么將格式設計成相互獨立的的自描述字節塊。

          11、注意浮點數取整。

          12、不要僅對文件的一部分進行壓縮或者二進制編碼。

          應用協議元格式

          1、經典的互聯網應用元協議 RFC 3117《論應用協議的設計》,如SMTPPOP3、IMAP等協議

          2、作為通用應用協議的HTTP,在HTTP上構建專用協議,如互聯網打印協議(IPP

          3、BEEP:塊可擴展交換協議,既支持C/S模式,又支持P2P模式

          4、XMP-RPC、SOAPJabber,基于XML的協議。


          透明性:來點光

          美在計算科學中的地位,要比在其他任何技術中的地位都重要,因為軟件是太復雜了。美是抵御復雜的終極武器

          如果沒有陰暗的角落和隱藏的深度,軟件系統就是透明的。透明性是一種被動品質。如果實際上能預測到程序行為的全部或大部分情況,并能建立簡單的心理模型,這個程序就是透明的,因為可以看透機器究竟在干什么。

          如果軟件系統所包含的功能是為了幫助人們對軟件建立正確的“做什么、怎樣做”的心理模型而設計,這個軟件系統就是可顯的。

          不要讓調試工具僅僅成為一種事后追加或者用過就束之高閣的東西。它們是通往代碼的窗口:不要只在墻上鑿出粗糙的洞,要修整這些洞并裝上窗。如果打算讓代碼一直可被維護,就始終必須讓光照進去。例如fetchmail-v選項將處理SMTP、POP的處理過程打印到標準輸出,使得fetchmail行為具有可顯性。

          在“這個設計能行嗎?”之后要提出的頭幾個問題就是“別人能讀懂這個設計嗎?這個設計優雅嗎?”我們希望,此時大家已經很清楚,這些問題不是廢話,優雅不是一種奢侈。在人類對軟件的反映中,這些品質對于減少軟件bug和提高軟件長期維護性是最基本的。

          要追求代碼的透明性,最有效的方法是很簡單,就是不要在具體操作的代碼上疊放太多的抽象層。

          OO語言使得抽象變得容易——也許是太容易了。OO語言鼓勵“具有厚重的膠合和復雜層次”的體系。當問題領域真的很復雜,確實需要大量抽象時,這可能是好事,但如果coder到頭來用復雜的辦法做簡單的事情——僅僅是為他們能夠這樣做,結果便適得其反。

          所有的OO語言都顯示出某種使程序員陷入過度分層陷阱的傾向。對象框架和對象瀏覽器并不能代替良好的設計和文檔,但卻常常被混為一談。過多的層次破壞了透明性:我們很難看清這些層次,無法在頭腦中理清代碼到底是怎樣運行的。簡潔、清晰和透明原則通通被破壞了,結果代碼中充滿了晦澀的bug,始終存在維護問題。

          膠合層中的“智能數據”卻經常不代表任何程序處理的自然實體——僅僅只是膠合物而已(典型現象就是抽象類和混入(mixin)的不斷擴散)

          OO抽象的另一個副作用就是程序往往喪失了優化的機會。

          OO在其取得成功的領域(GUI、仿真和圖形)之所以能成功,主要原因之一可能是因為在這些領域里很難弄錯類型的本體問題。例如,在GUI和圖形系統中,類和可操作的可見對象之間有相當自然的映射關系。

          Unix風格程序設計所面臨的主要挑戰就是如何將分離法的優點(將問題從原始的場景中簡化、歸納)同代碼和設計的薄膠合、淺平透層次結構的優點相組合。

          太多的OO設計就像是意大利空心粉一樣,把“is-a”have a的關系弄得一團糟,或者以厚膠合層為特征,在這個膠合層中,許多對象的存在似乎只不過是為了在陡峭的抽象金字塔上占個位置罷了。這些設計都不透明,它們晦澀難懂并且難以調試。

          為透明性和可顯性而編碼:

          1、程序調用層次中(不考慮遞歸)最大的靜態深度是多少?如果大于,就要當心。

          2、代碼是否具有強大、明顯的不變性質(約束)?不變性質幫助人們推演代碼和發現有問題的情況。

          3、每個API中各個函數調用是否正交?或者是否存在太多的magic flags或者模式位?

          4、是否存在一些順手可用的關鍵數據結構或全局唯一的記錄器,捕獲系統的高層次狀態?這個狀態是否容易被形象化和檢驗,還是分布在數目眾多的各個全局變量或對象中而難以找到?

          5、程序的數據結構或分類和它們所代表的外部實體之間,是否存在清晰的一對一映射。

          6、是否容易找到給定函數的代碼部分?不僅單個函數、模塊,還有整個代碼,需要花多少精力才能讀懂

          7、代碼增加了特殊情況還是避免了特殊情況?每一個特殊情況可能對任何其他特殊情況產生影響:所有隱含的沖突都是bug滋生的溫床。然而更重要的是,特殊情況使得代碼更難理解。

          8、代碼中有多少個magic number?通過審查是否很容易查出代碼中的限制(比如關鍵緩沖區的大?。??

          隱藏細節無法訪問細節有著重要區別。不要過度保護

          無論何時碰到涉及編輯某類復雜二進制對象的設計問題時,unix傳統都提倡首先考慮,是否能夠編寫一個能夠在可編輯的文本格式和二進制格式之間來回進行無損轉換的工具?這類工具可稱為文本化器(textualizer).

          寧愿拋棄、重建代碼也不愿修補那些蹩腳的代碼。

          “代碼是活代碼、睡代碼還是死代碼?”活代碼周圍存在一個非常活躍的開發社團。睡代碼之所以“睡著”,經常是因為對作者而言,維護代碼的痛苦超過了代碼本身的效用。死代碼則是睡得太久,重新實現一段等價代碼更容易。


          評論

          # re: 《Unix編程藝術》重讀筆記(二)  回復  更多評論   

          2011-03-30 16:06 by xufeng
          受益匪淺!
          主站蜘蛛池模板: 闸北区| 朔州市| 垣曲县| 准格尔旗| 屏边| 淄博市| 江川县| 会东县| 垦利县| 濉溪县| 台中市| 开江县| 乌拉特前旗| 石城县| 开封县| 新乐市| 冕宁县| 琼中| 旌德县| 景泰县| 东山县| 旬邑县| 湖南省| 金川县| 庄河市| 康平县| 霍邱县| 汤阴县| 眉山市| 边坝县| 万山特区| 安塞县| 射洪县| 田林县| 宁明县| 新余市| 盘山县| 都江堰市| 乐清市| 札达县| 新河县|