馬丁(Robert C. Martin)
第1章 整潔代碼
寫整潔代碼,需要遵循大量的小技巧,貫徹刻苦習(xí)得的“整潔感”。這種“代碼感”就是關(guān)鍵所在。有些人生而有之。有些人費(fèi)點(diǎn)勁才能得到。它不僅讓我們看到代碼的優(yōu)劣,還予我們以借戒規(guī)之力化劣為優(yōu)的攻略。
我喜歡優(yōu)雅和高效的代碼。代碼邏輯應(yīng)當(dāng)直截了當(dāng),叫缺陷難以隱藏;盡量減少依賴關(guān)系,使之便于維護(hù);依據(jù)某種分層戰(zhàn)略完善錯(cuò)誤處理代碼;性能調(diào)至最優(yōu),省得引誘別人做沒(méi)規(guī)矩的優(yōu)化,搞出一堆混亂來(lái)。整潔的代碼只做好一件事。
第3章 函數(shù)
函數(shù)中混雜不同抽象層級(jí),往往讓人迷惑。讀者可能無(wú)法判斷某個(gè)表達(dá)式是基礎(chǔ)概念還是細(xì)節(jié)。更惡劣的是,就像破損的窗戶,一旦細(xì)節(jié)與基礎(chǔ)概念混雜,更多的細(xì)節(jié)就會(huì)在函數(shù)中糾結(jié)起來(lái)。
我們想要讓代碼擁有自頂向下的閱讀順序。我們想要讓每個(gè)函數(shù)后面都跟著位于下一抽象層級(jí)的函數(shù),這樣一來(lái),在查看函數(shù)列表時(shí),就能偱抽象層級(jí)向下閱讀了。我把這叫做向下規(guī)則。
別害怕長(zhǎng)名稱。長(zhǎng)而具有描述性的名稱,要比短而令人費(fèi)解的名稱好。長(zhǎng)而具有描述性的名稱,要比描述性的長(zhǎng)注釋好。使用某種命名約定,讓函數(shù)名稱中的多個(gè)單詞容易閱讀,然后使用這些單詞給函數(shù)取個(gè)能說(shuō)清其功用的名稱。
寫代碼和寫別的東西很像。在寫論文或文章時(shí),你先想什么就寫什么,然后再打磨它。初稿也許粗陋無(wú)序,你就斟酌推敲,直至達(dá)到你心目中的樣子。我寫函數(shù)時(shí),一開(kāi)始都冗長(zhǎng)而復(fù)雜。有太多縮進(jìn)和嵌套循環(huán)。有過(guò)長(zhǎng)的參數(shù)列表。名稱是隨意取的,也會(huì)有重復(fù)的代碼。不過(guò)我會(huì)配上一套單元測(cè)試,覆蓋每行丑陋的代碼。然后我打磨這些代碼,分解函數(shù)、修改名稱、消除重復(fù)。我縮短和重新安置方法。有時(shí)我還拆散類。同時(shí)保持測(cè)試通過(guò)。最后,遵循本章列出的規(guī)則,我組裝好這些函數(shù)。我并不從一開(kāi)始就按照規(guī)則寫函數(shù)。我想沒(méi)人做得到。
每個(gè)系統(tǒng)都是使用某種領(lǐng)域特定語(yǔ)言搭建,而這種語(yǔ)言是程序員設(shè)計(jì)來(lái)描述那個(gè)系統(tǒng)的。函數(shù)是語(yǔ)言的動(dòng)詞,類是名詞。這并非是退回到那種認(rèn)為需求文檔中的名詞和動(dòng)詞就是系統(tǒng)中類和函數(shù)的最初設(shè)想的可怕的舊觀念。其實(shí)這是個(gè)歷史更久的真理。編程藝術(shù)是且一直就是語(yǔ)言設(shè)計(jì)的藝術(shù)。大師級(jí)程序員把系統(tǒng)當(dāng)作故事來(lái)講,而不是當(dāng)作程序來(lái)寫。他們使用選定編程語(yǔ)言提供的工具構(gòu)建一種更為豐富且更具表達(dá)力的語(yǔ)言,用來(lái)講那個(gè)故事。那種領(lǐng)域特定語(yǔ)言的一個(gè)部分,就是描述在系統(tǒng)中發(fā)生的各種行為的函數(shù)層級(jí)。在一種狡猾的遞歸操作中,這些行為使用它們定義的與領(lǐng)域緊密相關(guān)的語(yǔ)言講述自己那個(gè)小故事。
第4章 注釋
注釋并不像辛德勒的名單。它們并不“純?nèi)坏睾谩?。?shí)際上,注釋最多也就是一種必須的惡。若編程語(yǔ)言足夠有表達(dá)力,或者我們長(zhǎng)于用這些語(yǔ)言來(lái)表達(dá)意圖,就不那么需要注釋——也許根本不需要。
注釋的恰當(dāng)用法是彌補(bǔ)我們?cè)谟么a表達(dá)意圖時(shí)遭遇的失敗。注意,我用了“失敗”一詞。我是說(shuō)真的。注釋總是一種失敗。我們總無(wú)法找到不用注釋就能表達(dá)自我的方法,所以總要有注釋,這并不值得慶賀。
如果你發(fā)現(xiàn)自己需要寫注釋,再想想看是否有辦法翻盤,用代碼來(lái)表達(dá)。每次用代碼表達(dá),你都該夸獎(jiǎng)一下自己。每次寫注釋,你都該做個(gè)鬼臉,感受自己在表達(dá)能力上的失敗。
用整理代碼的決心替代創(chuàng)造廢話的沖動(dòng)吧。你會(huì)發(fā)現(xiàn)自己成為更優(yōu)秀、更快樂(lè)的程序員。
第5章 格式
當(dāng)有人查看底層代碼實(shí)現(xiàn)時(shí),我們希望他們?yōu)槠湔麧崱⒁恢录八兄降膶?duì)細(xì)節(jié)的關(guān)注而震驚。我們希望他們高高揚(yáng)起眉毛,一路看下去。我們希望他們感受到那些為之勞作的專業(yè)人士們。但若他們看到的只是一堆像是由酒醉的水手寫出的鬼畫符,那他們多半會(huì)得出結(jié)論,認(rèn)為項(xiàng)目其他任何部分也同樣對(duì)細(xì)節(jié)漠不關(guān)心。
想想看寫得很好的報(bào)紙文章。你從上到下閱讀。在頂部,你期望有個(gè)頭條,告訴你故事主題,好讓你決定是否要讀下去。第一段是整個(gè)故事的大綱,給出粗線條概述,但隱藏了故事細(xì)節(jié)。接著讀下去,細(xì)節(jié)漸次增加,直至你了解所有的日期、名字、引語(yǔ)、說(shuō)法及其他細(xì)節(jié)。
第6章 對(duì)象和數(shù)據(jù)結(jié)構(gòu)
著名的得墨忒耳律(The Law ofDemeter)認(rèn)為,模塊不應(yīng)了解它所操作對(duì)象的內(nèi)部情形。如上節(jié)所見(jiàn),對(duì)象隱藏?cái)?shù)據(jù),曝露操作。這意味著對(duì)象不應(yīng)通過(guò)存取器曝露其內(nèi)部結(jié)構(gòu),因?yàn)檫@樣更像是曝露而非隱藏其內(nèi)部結(jié)構(gòu)。
對(duì)象曝露行為,隱藏?cái)?shù)據(jù)。便于添加新對(duì)象類型而無(wú)需修改既有行為,同時(shí)也難以在既有對(duì)象中添加新行為。數(shù)據(jù)結(jié)構(gòu)曝露數(shù)據(jù),沒(méi)有明顯的行為。便于向既有數(shù)據(jù)結(jié)構(gòu)添加新行為,同時(shí)也難以向既有函數(shù)添加新數(shù)據(jù)結(jié)構(gòu)。在任何系統(tǒng)中,我們有時(shí)會(huì)希望能夠靈活地添加新數(shù)據(jù)類型,所以更喜歡在這部分使用對(duì)象。另外一些時(shí)候,我們希望能靈活地添加新行為,這時(shí)我們更喜歡使用數(shù)據(jù)類型和過(guò)程。優(yōu)秀的軟件開(kāi)發(fā)者不帶成見(jiàn)地了解這種情形,并依據(jù)手邊工作的性質(zhì)選擇其中一種手段。
第8章 邊界
我們很少控制系統(tǒng)中的全部軟件。有時(shí)我們購(gòu)買第三方程序包或使用開(kāi)放源代碼,有時(shí)我們依靠公司中其他團(tuán)隊(duì)打造組件或子系統(tǒng)。不管是哪種情況,我們都得將外來(lái)代碼干凈利落地整合進(jìn)自己的代碼中。本章將介紹一些保持軟件邊界整潔的實(shí)踐手段和技巧。
邊界上會(huì)發(fā)生有趣的事。改動(dòng)是其中之一。有良好的軟件設(shè)計(jì),無(wú)需巨大投入和重寫即可進(jìn)行修改。在使用我們控制不了的代碼時(shí),必須加倍小心保護(hù)投資,確保未來(lái)的修改不至于代價(jià)太大。
第9章 單元測(cè)試
整潔的測(cè)試有什么要素?有三個(gè)要素:可讀性,可讀性和可讀性。在單元測(cè)試中,可讀性甚至比在生產(chǎn)代碼中還重要。測(cè)試如何才能做到可讀?和其他代碼中一樣:明確,簡(jiǎn)潔,還有足夠的表達(dá)力。在測(cè)試中,你要以盡可能少的文字表達(dá)大量?jī)?nèi)容。
第10章 類
遵循標(biāo)準(zhǔn)的Java約定,類應(yīng)該從一組變量列表開(kāi)始。如果有公共靜態(tài)常量,應(yīng)該先出現(xiàn)。然后是私有靜態(tài)變量,以及私有實(shí)體變量。很少會(huì)有公共變量。公共函數(shù)應(yīng)跟在變量列表之后。我們喜歡把由某個(gè)公共函數(shù)調(diào)用的私有工具函數(shù)緊隨在該公共函數(shù)后面。這符合了自頂向下原則,讓程序讀起來(lái)就像一篇報(bào)紙文章。
第11章 系統(tǒng)
系統(tǒng)也應(yīng)該是整潔的。侵害性架構(gòu)會(huì)湮滅領(lǐng)域邏輯,沖擊敏捷能力。當(dāng)領(lǐng)域邏輯受到困擾,質(zhì)量也就堪憂,因?yàn)槿毕莞纂[藏,用戶故事更難實(shí)現(xiàn)。當(dāng)敏捷能力受到損害時(shí),生產(chǎn)力也會(huì)降低,TDD的好處遺失殆盡。在所有的抽象層級(jí)上,意圖都應(yīng)該清晰可辨。只有在編寫POJO并使用類方面的機(jī)制來(lái)無(wú)損地組合其他關(guān)注面時(shí),這種事情才會(huì)發(fā)生。無(wú)論是設(shè)計(jì)系統(tǒng)或單獨(dú)的模塊,別忘了使用大概可工作的最簡(jiǎn)單方案。
第12章 迭進(jìn)
運(yùn)行所有測(cè)試;不可重復(fù);表達(dá)了程序員的意圖;盡可能減少類和方法的數(shù)量;
全面測(cè)試并持續(xù)通過(guò)所有測(cè)試的系統(tǒng),就是可測(cè)試的系統(tǒng)??此茰\顯,但卻重要。不可測(cè)試的系統(tǒng)同樣不可驗(yàn)證。不可驗(yàn)證的系統(tǒng),絕不應(yīng)部署。
第17章 味道與啟發(fā)
如果沒(méi)有if或while語(yǔ)句的上下文,布爾邏輯就難以理解。應(yīng)該把解釋了條件意圖的函數(shù)抽離出來(lái)。
否定式要比肯定式難明白一些。所以,盡可能將條件表示為肯定形式。