#Unix哲學強調實用性,源自於豐富經驗,不受傳統方法學或標準限制。這種知識更像是潛在的、半本能的。 Unix程式設計師透過開發經驗累積的知識可讓其他程式設計師受益。
(1) 每個程式應專注於完成一項任務,遇到新任務時應重新開始,避免在原始程式中新增功能而導致複雜度增加。 (2) 假設程式的輸出將成為另一個程式的輸入,即使下一個程式尚不清楚,也應確保輸出中不包含無關資訊。 (3) 儘早將設計和編寫的軟體投入試用,對低品質程式碼應果斷放棄並重新編寫。 (4) 使用工具優先於低效率的輔助手段來減輕程式設計任務的負擔,精益求精,必須善用工具。
Unix 哲學的精髓並非僅僅是先賢們口頭傳達的,更體現在他們的實踐和Unix系統本身的設計。這一哲學可以概括為幾個關鍵點:
初學軟體工程時,應深入體會這些原則。雖然大部分文章都推崇這些準則,但許多系統缺乏實務工具和傳統,導致程式設計師無法執行這些原則。他們常常受制於糟糕的工具、設計不良、過度勞累和冗餘程式碼。
程式設計的核心在於管理複雜性。解決錯誤佔據了大部分開發時間。一個可用系統的成功更多是由於不斷嘗試、調整,而不僅僅是天賦或設計技巧。
彙編語言、編譯語言、流程圖、流程化程式設計、結構化程式設計、物件導向、以及軟體開發方法論,過度吹捧。但它們卻增加了程序的複雜度,超出人腦處理能力。
為了成功開發複雜軟體,降低整體複雜度並透過簡單模組的清晰介面組合是關鍵。這樣可以使問題局限在特定部分,從而更容易改善局部而不影響整體。
#編寫程式碼時要考慮到未來維護的複雜性和成本。程式碼應該易於閱讀和理解,以便他人或自己在必要時能夠輕鬆修改和維護。
在Unix傳統中,這個原則不只適用於程式碼註解。在Unix的最佳實踐中,也強調在選擇演算法和實作時要考慮到未來的可擴展性。儘管為了稍微提升程序效能而增加技術複雜性和混亂性可能誘人,但這種做法是得不償失的。這不僅因為複雜的程式碼容易引發bug,還因為它會增加日後的閱讀和維護難度。相反,優雅且清晰的程式碼不僅更加穩定,還更易於其他人理解和修改。這一點至關重要,特別是因為可能在未來幾年內需要回頭修改這些代碼的人可能會是你自己。
永遠不要去吃力地解讀一段晦澀的程式碼三次。第一次也許僥倖成功,但如果發現必須重新解讀一遍──離第一次太久了,具體細節無從回想,那就該註解程式碼了,這樣第三次就相對不會那麼痛苦了。
#如果程式彼此之間無法有效通信,那麼軟體就難免會陷入複雜度的泥淖。
在輸入輸出方面, Unix 傳統極力提倡採用簡單、文字化、面向流、裝置無關的格式。在經典的 Unix 下,多數程式都盡可能採用簡單過濾器的形式,即將一個輸入的文字流處理為一個簡單的文字流輸出。撇開世俗眼光,Unix程式設計師偏愛這種做法並不是因為他們仇視圖形使用者介面,而是因為如果程式不採用簡單的文字輸入輸出流,它們就極難銜接。
Unix 中文字流之於工具,就如同在物件導向環境中的訊息之於物件。文字流介面的簡潔性加強了工具的封裝性。而許多精緻的進程間通訊方法,例如遠端過程調用,都存在著各程式牽扯過多的傾向。
要讓程式具有組合性,就要使程式彼此獨立。在文字流這一端的程式應該盡可能不要考慮文字流另一端的程式。將一端的程序替換為另一個截然不同的程序,而完全不驚擾另一端應該很容易做到。 GUI 可以是個好東西,在做GUI前,應該想想可不可以把複雜的交互程序跟乾粗活的算法程序分離開,每個部分單獨成為一塊,然後用一個簡單的命令流或者是應用協議將其組合在一起。
在構思精巧的資料傳輸格式前,有必要實地考察一下,是否能利用簡單的文字資料格式;以一點點格式解析的代價,換得可以使用通用工具來建構或解讀資料流的好處是值得的。
當程式無法自然地使用序列化、協定形式的介面時,正確的 Unix 設計至少是,把盡可能多的程式設計元素組織為一套定義良好的 API 。這樣至少可以透過連結呼叫應用程序,或根據不同任務的需求黏合使用不同的介面。
策略和機制是依照不同的時間尺度變化的,策略的變化要遠遠快於機制。把策略同機制揉成一團有兩個負面影響:一來會讓策略變得死板,難以適應使用者需求的改變,二來也意味著任何策略的改變都極有可能動搖機制。相反,將兩者剝離,就有可能在探索新策略的時候不足以打破機制。另外,也更容易為機制寫出較好的測驗。
實現剝離的一個方法,將應用程式分成可以協作的前端和後端進程,透過套接字上層的專用應用協定進行通訊。前端實作策略,後端實現機制。比起僅用單一進程的整體實現方式來說,這種雙端設計方式大大降低了整體複雜度 ,bug 有望減少,從而降低程式的壽命週期成本。
來自多方面的壓力常常會讓程序變得複雜(由此代價更高, bug 更多),其中 一種壓力就是來自技術上的虛榮心理。程式設計師都很聰明,常常以能玩複雜東西和耍弄抽象概念的能力為傲,這一點也無可厚非。但正因如此常常會與同行們比試,看看 誰能夠鼓搗出最錯綜複雜的美妙事物,他們的設計能力大大超出他們的實現和排錯能力,結果便是代價高昂的廢品。
」錯綜複雜的美妙事物」聽來自相矛盾。 Unix 程式設計師相互比的是誰能做到 “簡潔而漂亮”,這一點雖然只是隱含在這些規則之中,但還是很值得公開提出來強調一下。
至少在商業軟體領域裡,過度的複雜性往往來自於專案的要求,而這些要求常常是基於推銷熱點,而不是基於顧客的需求和軟體實際上能夠提供的功能。許多優秀的設計被市場推銷所需的大堆「特性清單」扼殺,實際上這些特性功能幾乎從未使用過。然後,惡性循環開始了,比別人花俏的方法就是把自己變得更花俏。很快,龐大臃腫變成了業界標準,每個人都在使用臃腫不堪、bug 極多的軟體,連軟體開發人員也不敢敝帚自珍。
避免這些陷阱,唯一的方法就是鼓勵另一種軟體文化,以簡潔為美。這是一個非常重視簡單解決方案的工程傳統,總是設法將程式系統分解為幾個能夠協作的小部分,並本能地抵制任何用過多噱頭來粉飾程式的企圖。
「大」有兩重意義:體積大,複雜程度高。程序大了,維護就困難。由於對花費了大量精力才做出來的東西難以割捨,結果導致在龐大的程序中,把投資浪費在註定要失敗,或並非最佳的方案上。避免不必要的程式碼和邏輯,保持程式碼精簡。
因為調試通常會佔用四分之三甚至更多的開發時間,所以一開始就多做點工作以減少日後調試的工作量會很划算。一個有效的減少調試工作量的方法,就是設計時充分考慮透明性和顯見性。
軟體系統的透明性是指一眼就能看出軟體在做什麼以及怎麼做的。顯見性指程式帶有監視和顯示內部狀態的功能,這樣程式不僅能夠運作良好,而且還可以看出它以何種方式運作。
設計時如果充分考慮到這些要求會為整個專案全過程帶來好處。調試選項的設定盡量不要在事後,而應該在設計之初便考慮進去,程序不但應該能展示其正確性,也應該能把原開發者解決問題的思維模型告訴後來者。
程式如果要展示其正確性,應該使用足夠簡單的輸入輸出格式,這樣才能保證很容易地檢驗有效輸入和正確輸出之間的關係是否正確。出於充分考慮透明性和顯見性的目的,也應該提倡介面簡潔,以方便其它程式對其進行操作,尤其是測試監視工具和偵錯腳本。關注微信公眾號【嵌入式系統】
軟體的健壯性指軟體不僅能在正常情況下運作良好,而且在超出設想的意外條件下也能夠運作良好。
大多數軟體禁不起磕碰,毛病多,就是因為過於複雜,很難通盤考慮。如果不能正確理解一個程式的邏輯,就不能確信其是否正確,也就不能在出錯的時候修復它。讓程式健壯的方法,就是讓程式的內部邏輯更容易理解,要做到這一點主要有兩種方法:透明化和簡潔化。
就健壯性而言,設計時要考慮到能承受極端輸入,這一點也很重要。在有異常輸入的情況下,確保軟體健全性的一個相當重要的策略就是避免在程式碼中出現特例,bug 通常隱藏在處理特例的程式碼以及處理不同特殊情況的互動操作部分的程式碼中。
軟體的透明性就是指一眼就能夠看出來是怎麼回事。如果「怎麼回事」不算複雜,即不需要絞盡腦汁就能夠推斷出所有可能的情況,那麼這個程序就是簡潔的。程式越簡潔,越透明,也越健壯。
模組化(程式碼簡樸,介面簡潔)是組織程式以達到更簡潔目的的方法。
#資料要比程式邏輯更容易駕馭,在設計中,應該主動將程式碼的複雜度轉移到資料之中去。
此種考量並非 Unix 的原創,但許多 Unix 程式碼都顯示受其影響。特別是C 語言對指標使用控制的功能,促進了在核心以上各個編碼層面上對動態修改引用結構。在結構中用非常簡單的指標操作就能夠完成的任務,在其它語言中,往往必須用更複雜的過程才能完成。
進行資料驅動程式設計時,需要把程式碼和程式碼作用的資料結構劃分清楚,這樣,改變程式的邏輯時,只要編輯資料結構而不是程式碼。資料驅動程式設計有時會跟著物件導向混淆起來,後者是另一種以資料組織為中心的風格。它們之間至少有兩點不同。第一,在資料驅動程式設計中,資料不只是某個物件的狀態,實際上還定義了程式的控制流程;第二,物件導向首先考慮的是封裝,而資料驅動程式設計看重的是編寫盡可能少的固定代碼。
也就是眾所周知的「最少驚奇原則」。最容易使用的程序就是使用者需要學習新東西最少的程序,就是最切合使用者已有知識的程序。因此,介面設計應該避免毫無來由的標新立異和自作聰明。
如果你編製一個計算器 程序, ‘ ’應該永遠表示加法。設計介面的時候,盡量按照使用者最可能熟悉的同樣功能介面和相似應用程式來進行建模。
專注於目標受眾,他們也許是最終用戶,也許是其他程式設計師,也許是系統管理員。對於這些不同的人群,最少驚奇的意義也不同。關注傳統慣例,這些慣例的存在有個極好的理由:緩和學習曲線。
最小立異原則的另一面是避免表象相似而實際卻略有不同。這會極度危險, 因為表象相似往往導致人們產生錯誤的假定。所以最好讓不同事物有明顯差別,而不要看起來幾乎一模一樣。
#行為良好的程序應該默默工作,絕不嘮叨。沉默是金,這個原則的起始是源自於Unix 誕生時還沒有視訊顯示器,每一行多餘的輸出都會嚴重消耗用戶的寶貴時間。現在這種情況已不復存在, 但一切從簡的這個優良傳統流傳至今。
簡潔是 Unix 程式的核心風格。一旦程式的輸出成為另一個程式的輸 入,就很容易把需要的資料挑出來。站在人的角度上來說,重要資訊不應該混雜在冗長的程序內部行為資訊中。如果顯示的資訊都是重要的,那就不用找了。設計良好的程式將使用者的注意力視為有限的寶貴資源,只有在必要時才要求使用,避免不必要的資訊對使用者的打擾。
#軟體在發生錯誤的時候也應該與在正常操作的情況下一樣,有透明的邏輯。最理想的情況當然是軟體能夠適應和應付非正常操作;而如果補救措施明明沒有成功,卻悄無聲息地埋下崩潰的隱患,直到很久以後才顯現出來,這就是最壞的一種情況。
因此,軟體要盡可能從容地應付各種錯誤輸入和自身的運行錯誤,如果做不到這一點,就讓程式盡可能以一種容易診斷錯誤的方式終止。
“宽容地收,谨慎地发”。就算输入的数据不规范,设计良好的程序也会尽量领会其中的意义,尽量与别的程序协作;然后,要么响亮地倒塌,要么为工作链下一环的程序输出一个严谨干净正确的数据。
设计时要考虑宽容性,不是用过分纵容的实现来补救标准的不足,否则一不留神你会死得很难看。
在 Unix.早期的小型机时代,这一条观点还是相当激进的;随着技术的发展,开发公司和大多数用户都能够得到廉价的机器,所以这一准则的合理性就不用多说。
在保证质量的前提下,尽量使用计算机资源完成任务,减轻程序员负担,另一个可以显著节约程序员时间的方法是,教会机器如何做更多低层次的编程工作。关注微信公众号【嵌入式系统】
众所周知,人类很不善于干辛苦的细节工作。程序中的任何手工操作都是滋生错误和延误的温床,由程序生成代码几乎总是比手写代码廉价并且更值得信赖。
对于代码生成器来说,需要手写的重复而麻木的高级语言代码,与机器码一样是可以批量生产的。当代码生成器能够提升抽象度时,即当生成器的说明性语句要比生成码简单时,使用代码生成器会很合算,而生成代码后就根本无需再费力地去手工处理。在 Unix 中大量使用代码生成器使易于出错的细节工作自动化。
原型设计最基本的原则,“90%的功能现在能实现,比100%的功能永远实现不了强”。做好原型设计可以避免为蝇头小利而投入过多的时间。
“ 不应考虑蝇头小利的效率提升,过早优化是万恶之源”,不知道瓶颈所在就匆忙进行优化,这可能是唯一一个比乱加功能更损害设计的错误。从畸形的代码到杂乱无章的数据布局,牺牲透明性和简洁性而片面追求速度,滋生无数 bug, 耗费以百万计的人时,这点芝麻大的好处,远不能抵消后续排错所付出的代价。
过早的局部优化实际上会妨碍全局优化,从而降低整体性能。在整体设计中可以带来更多效益的修改常常会受到一个过早局部优化的干扰,导致出来的产品既性能低劣又代码过于复杂。
在 Unix 世界里,有一个非常明确的悠久传统:先制作原型,再精雕细琢。优化之前先确保能用,先能走,再学跑。从另一种不同的文化将这一点有效地扩展为:先求运行,再求正确,最后求快。
所有这些话的实质其实是一个意思:先设计做个未优化的、运行缓慢、很耗内存但是正确的实现,然后进行系统地调整,寻找那些可以通过牺牲最小的局部简洁性而获得较大性能提升的地方。
即使最出色的软件也常常会受限于设计者的想象力。没有人能聪明到把所有东西都最优化,也不可能预想到软件所有可能的用途。
對於軟體設計和實作來說,Unix 傳統有一點很好,就是從不相信任何所謂的「不二法門」。 Unix 奉行的是廣泛採用多種語言、開放的可擴展系統和用戶定制機制;吸收並借鑒各種優秀的設計思想,不斷完善自己的設計方法和風格。
為資料格式和程式碼留下擴充的空間,否則,就會發現常常被原先的不明智選擇捆住了手腳,因為無法既要改變它們又要維持對原來的兼容性。
設計協定或文件格式時,應使其具有充分的自描述性以便可擴展。要麼包含版本號,要麼採用獨立、自描述的語句,按照可以隨時插入新的、換掉舊的,而不會破壞格式讀取程式碼的方法組織格式。 Unix 經驗表示:稍微增加一點讓資料部署具有自描述性的開銷,就可以在無需破壞整體的情況下進行擴展,小的付出也可得到成千倍的回報。
設計程式碼時,要有很好的組織,讓未來的開發者增加新功能時無需拆毀或重建整個架構。這個原則並不是說隨意增加根本用不上的功能,而是建議在編寫程式碼時要考慮到將來的需要,使以後增加功能比較容易。程式接合部要靈活,在程式碼中加入「如果擴充…需要…」的註解,有義務給之後使用和維護自己寫的程式碼的人做點好事,也許將來就是自己來維護程式碼,設計為將來著眼,節省的有可能就是自己的精力。
#這些富有哲理的原則絕不是模糊籠統的泛泛之談。在Unix 世界中,這些原則都直接來自於實踐,並形成了具體的規定。
運用Unix 哲學,就應該不斷追求卓越。軟體設計是一門技藝,值得付出智慧、創意和熱情。否則就不會超越那些簡單、老套的設計和實現;就會在應該思考的時候急急忙忙去編程,就會在該無情刪繁就簡的時候反而把問題複雜化,就會反過來抱怨代碼怎麼那麼臃腫、難以調試。
要好好運用 Unix 哲學,永遠不要蠻幹;要多用巧勁,省下力氣到需要的時候再用,好鋼用在刀刃上。善用工具,盡可能將一切自動化。
軟體設計和實現是一門充滿快樂的藝術, 一種高水準的遊戲。為什麼要從事軟體設計而不是別的呢?可能現在只是為了賺錢或打發時間,也可能曾經也認為軟體設計改變世界,值得付出激情。
以上是Unix哲學之程式原則的詳細內容。更多資訊請關注PHP中文網其他相關文章!