〓● 如果代碼可讀性不佳、不容易理解,可能造成如下問題:
〓? 其他工程師浪費時間解讀它;
〓? 誤解導致引入缺陷;
〓? 其他工程師修改時破壞代碼。
〓● 提高代碼可讀性,有時候可能使其變得更為冗長、占用更多的代碼行數。這往往是有價值的權衡。
〓● 提高代碼可讀性往往需要同理心——想象其他人可能覺得困惑的情況。
〓● 現實生活中的場景各不相同,通常有各自面臨的挑戰。編寫易于理解的代碼幾乎總是需要應用常識和判斷力。
編寫易于理解的代碼
實現代碼可讀性最為常見、有效的技術打下堅實的基礎。但需要牢記的是,現實生活中的每個場景都不相同,有各自的考慮因素,因此常識的運用和良好的判斷力都是必不可少的。
1 使用描述性名稱
名稱是唯一標識事物所必需的,它們也往往能提供對事物概念的簡單總結。“烤箱”這個詞是某個廚房用具的唯一標識,但也明顯地暗示了這件用具的用途:燒烤食物。如果我們堅持用“對象A”來指代一臺烤箱,就很容易忘記“對象A”是什么東西,起什么作用。
在代碼中命名不同事物也適用相同的原則。名稱是唯一標識類、函數和變量等對象所必需的。但我們對事物的命名也提供了很好的機會,可以通過確保以不言自明的方式指代事物,使代碼更易于理解。
2 適當使用注釋
代碼中的注釋或文檔可以起到多種作用,如:
〓● 解釋代碼完成的是什么;
〓● 解釋代碼為什么完成這些工作;
〓● 提供其他信息,如使用指南。
本節將集中說明前兩個作用:使用注釋解釋“什么”和“為什么”。使用說明等其他信息通常組成代碼契約的一部分,這些已在第3章討論過。
概述大塊代碼(如一個類)作用的高層注釋常常很有用。然而,在較低層次的代碼細節上,解釋代碼作用的注釋往往不是提高代碼可讀性的最有效手段。
在代碼行級別的作用上,使用描述性名稱、編寫質量良好的代碼應該是不言自明的。如果我們需要為代碼添加許多底層注釋以解釋它的作用,那么這很可能是代碼可讀性不理想的跡象。相反,解釋代碼為什么存在或提供更多背景信息的注釋往往相當有用,因為只用代碼不總是能夠說明這些。
3 不要執著于代碼行數
一般來說,代碼庫中的代碼行數越少越好。代碼通常需要一定的持續維護,代碼行數越多,有時就意味著代碼過于復雜,或者沒有重用現有的解決方案。較多的代碼還會增加工程師的認知負荷,因為閱讀量顯然更大了。
工程師有時會在這方面走極端,認為最大限度地減少代碼行數比代碼質量的其他因素更重要。他們有時會抱怨,所謂的“代碼質量改善”將3行代碼變成10行代碼,因此產生的代碼更差了。
但應該牢記的是,代碼行數只是我們真正關心的事情的一個替代指標,與大部分替代指標一樣,這是個有用的指導原則,但并非鐵律。我們真正關心的是確保代碼:
〓● 容易理解;
〓● 不容易受到誤解;
〓● 不容易在無意中遭到破壞。
并不是所有代碼都是一樣的:與10行(甚至20行)易于理解的代碼相比,1行極其難以理解的代碼更容易降低代碼質量。5.3.1節和5.3.2節用例子闡述了這一點。
4 堅持一致的編程風格
在我們造句的時候,必須遵循某些規則,才能寫出語法正確的句子。此外,我們應該遵循一些其他風格上的指南,以確保我們的句子容易理解。
舉個例子,想象一下我們要寫段關于“軟件即服務”(Software as a service)的文字。通常,如果一個首字母縮略詞包含“a”或“as”等單詞,那么這些單詞應該用小寫字母形式。因此,人們最熟悉的“軟件即服務”縮寫為SaaS。如果我們將這個縮略詞寫成SAAS,閱讀文檔的人可能會誤以為我們指的是其他事物,因為那不是他們預期中的“軟件即服務”的縮寫。
這也同樣適用于代碼。語言的語法和編譯器規定了允許的寫法(有點像語法規則),但在工程師編寫代碼時,我們對于采用的風格慣例有很大的自由度。
5 避免深嵌套代碼
典型的代碼由相互嵌套的塊組成,如:
〓● 函數定義一個調用時運行的代碼塊;
〓● if語句定義條件為真時運行的代碼塊;
〓● for循環定義每次迭代時運行的代碼塊。
圖6 使函數調用易于理解
如果函數命名得當,它的作用應該是顯而易見的,但即便有了這樣的函數,如果參數的目的和作用不明,函數調用也很可能無法理解。1說明了控制流邏輯(如if語句和for循環)造成代碼塊相互嵌套的情況。代碼中的指定邏輯通常有不止一種構造方法。有些形式可能造成許多代碼塊嵌套,而其他方法可能幾乎不會造成任何嵌套。考慮代碼結構對可讀性的影響是很重要的。
1 控制流邏輯(如if語句和for循環)可能造成代碼塊相互嵌套
6 使函數調用易于理解
如果函數命名得當,它的作用應該是顯而易見的,但即便有了這樣的函數,如果參數的目的和作用不明,函數調用也很可能無法理解。
7 避免使用未做解釋的值
在許多情況下我們需要硬編程的值。常見例子如下:
〓● 數值轉換中的系數;
〓● 可調整的參數,例如某項任務失敗時的最大重試次數;
〓● 代表某些值填寫模板的字符串。
所有硬編程值都有兩個重要的信息:
〓●?具體值——計算機在執行代碼時必須知道這一信息;
〓●?值的意義——工程師只有知道了這一信息,才能理解代碼。沒有這一信息,工程師將無法理解代碼。
代碼需要一個顯而易見的值,否則代碼很可能無法編譯或起作用,但工程師在編寫該值的時候很容易忘記向其他工程師澄清該值的實際含義。
8 正確使用匿名函數
匿名函數是沒有名稱的函數。它們通常在需要的時候于代碼中內嵌定義。不同編程語言定義匿名函數的語法也各不相同。
9 正確使用新奇的編程語言特性
每個人都喜歡新鮮的事物,工程師也不例外。許多編程語言仍在積極發展,而編程語言設計師也在不斷增加令人喜愛的新特性。當這種情況發生時,工程師往往渴望利用新特性。
編程語言設計師在增加新特性之前經過了非常謹慎的思考,因此在許多情況下,新特性都可能使代碼更加易于理解或者魯棒。工程師因為這些新特性而興奮是很好的事情,因為這增大了利用新特性改善代碼的可能性。但如果你發現自己渴望使用新奇的編程語言特性,一定要保持坦誠的態度,思考這一特性是不是適合手頭使用的工具。
推薦書籍
- 《代碼整潔之道》(Clean Code):這本書強調了編寫干凈、可維護代碼的重要性,并提供了一些實用的編程技巧和原則。
? - 《代碼整潔之道:程序員的職業素養》(Clean Coder: A Code of Conduct for Professional Programmers):這本書不僅關注于編寫干凈的代碼,還強調了程序員應該遵循的一些職業素養和道德準則。
? - 《好代碼 ,壞代碼》:本書教你如何像高效的軟件工程師一樣思考代碼,如何編寫讀起來像一個結構良好的句子的函數,如何確保代碼可靠且無錯誤;如何進行有效的單元測試,如何識別可能導致問題的代碼并對其進行改進,如何編寫可重用并適應新需求的代碼,如何提高讀者的中長期生產力;同時還介紹了如何節省開發人員及團隊的寶貴時間,等等。