讀書筆記—《編寫可讀程式碼的藝術》
- 2019 年 10 月 27 日
- 筆記
前言
我們曾經在非常成功的軟體公司中和出色的工程師一起工作,然而我們所遇到的程式碼仍有很大的改進空間。實際上,我們曾見到一些很難看的程式碼,你可能也見過。但是當我們看到寫得很漂亮的程式碼時,會很受啟發。好程式碼會很明確告訴你它在做什麼。使用它會很有趣,並且會鼓勵你把自己的程式碼寫得更好。本書旨在幫助你把程式碼寫得更好。
每一章都會深入編程的某個方面來討論如何使程式碼更容易理解。本書分成四部分:表面層次上的改進命名、注釋以及審美——可以用於程式碼庫每一行的小提示。簡化循環和邏輯在程式中定義循環、邏輯和變數,從而使得程式碼更容易理解。重新組織你的程式碼在更高層次上組織大的程式碼塊以及在功能層次上解決問題的方法。
第1章 程式碼應當易於理解
-
程式碼的寫法應當使別人理解它所需的時間最小化。
- 減少程式碼行數是一個好目標,但把理解程式碼所需的時間最小化是一個更好的目標。
-
經常想一想其他人是不是會覺得你的程式碼容易理解
第一部分 表面層次的改進
- 選擇好的名字
- 寫好的注釋
- 把程式碼整潔地寫成更好的格式
第2章 把資訊裝到名字里
- 把資訊裝進名字中
- 選擇專業的詞
- 避免使用tmp、retval、it、foo泛泛的詞
- 如果不把循環索引命名為(i、j、k),另一個選擇可以是(club_i、members_i、user_i)或者,更簡化一點(ci、mi、ui)
- 用具體的名字替代抽象的名字
- 使用前綴和後綴來給名字附帶更多資訊
- 決定名字的長度
- 在小的作用域里可以使用短的名字
- 利用名字的格式來表達含義
第3章 不會誤解的名字
-
filter
是個二義性單詞。我們不清楚它的含義到底是「挑出」還是「減掉」。最好避免使用「filter」這個名字,因為它太容易誤解。 -
推薦用min和max來表示(包含)極限
-
建議 命名極限最清楚的方式是在要限制的東西前加上max_或者min_。
-
推薦用first和last來表示包含的範圍
-
推薦用begin和end來表示包含/排除範圍
-
通常來講,加上像is、has、can或should這樣的詞,可以把布爾值變得更明確。
-
很多程式設計師都習慣了把以get開始的方法當做「輕量級訪問器」這樣的用法,它只是簡單地返回一個內部成員變數。如果違背這個習慣很可能會誤導用戶。相反,這個方法應當重命名為像computeMean()這樣的名字,後者聽起來更像是有些代價的操作。
第4章 審美
大家都願意讀有美感的程式碼。通過把程式碼用一致的、有意義的方式「格式化」,可以把程式碼變得更容易讀,並且可以讀得更快。下面是討論過的一些具體技巧:
- 如果多個程式碼塊做相似的事情,嘗試讓它們有同樣的剪影,使用一致的布局,讓讀者很快就習慣這種風格
- 把程式碼按「列」對齊可以讓程式碼更容易瀏覽
- 如果在一段程式碼中提到A、B和C,那麼不要在另一段中說B、C和A。選擇一個有意義的順序,並始終用這樣的順序
- 用空行來把大塊程式碼分成邏輯上的「段落」。
第5章 該寫什麼樣的注釋
注釋的目的是幫助讀者了解作者在寫程式碼時已經知道的那些事情。本章介紹了如何發現所有的並不那麼明顯的資訊塊並且把它們寫下來。什麼地方不需要注釋:
- 能從程式碼本身中迅速地推斷的事實
- 用來粉飾爛程式碼(例如蹩腳的函數名)的「拐杖式注釋」——應該把程式碼改好。你應該記錄下來的想法包括:
- 對於為什麼程式碼寫成這樣而不是那樣的內在理由(「指導性批註」)
- 程式碼中的缺陷,使用像TODO:或者XXX:這樣的標記
- 常量背後的故事,為什麼是這個值。
- 站在讀者的立場上思考:預料到程式碼中哪些部分會讓讀者說:「啊?」並且給它們加上注釋
- 為普通讀者意料之外的行為加上注釋
- 在文件/類的級別上使用「全局觀」注釋來解釋所有的部分是如何一起工作的
- 用注釋來總結程式碼塊,使讀者不致迷失在細節中
第6章 寫出言簡意賅的注釋
本章是關於如何把更多的資訊裝入更小的空間里。下面是一些具體的提示:
- 當像「it」和「this」這樣的代詞可能指代多個事物時,避免使用它們
- 盡量精確地描述函數的行為
- 在注釋中用精心挑選的輸入/輸出例子進行說明
- 聲明程式碼的高層次意圖,而非明顯的細節
- 用嵌入的注釋(如Function(/arg =/…))來解釋難以理解的函數參數
- 用含義豐富的詞來使注釋簡潔
第二部分 簡化循環和邏輯
第7章 把控制流變得易讀
有幾種方法可以讓程式碼的控制流更易讀
- 在寫一個比較時(while (bytes_expected > bytes_received)),把改變的值寫在左邊並且把更穩定的值寫在右邊更好一些(while (bytes_received <; bytes_expected))
- 你也可以重新排列if/else語句中的語句塊。通常來講,先處理正確的/簡單的/有趣的情況。有時這些準則會衝突,但是當不衝突時,這是要遵循的經驗法則
- 某些編程結構,像三目運算符(:?)、do/while循環,以及goto經常會導致程式碼的可讀性變差。最好不要使用它們,因為總是有更整潔的代替方式
- 嵌套的程式碼塊需要更加集中精力去理解。每層新的嵌套都需要讀者把更多的上下文「壓入棧」。應該把它們改寫成更加「線性」的程式碼來避免深嵌套。通常來講提早返回可以減少嵌套並讓程式碼整潔。「保護語句」(在函數頂部處理簡單的情況時)尤其有用。
第8章 拆分超長表達式
- 把超長表達式拆成更容易理解的小塊
- 引入解釋變數
第9章 變數與可讀性
關於程式中的變數是如何快速累積而變得難以跟蹤的。你可以通過減少變數的數量和讓它們盡量「輕量級」來讓程式碼更有可讀性。具體有:
- 減少變數,即那些妨礙的變數。我們給出了幾個例子來演示如何通過立刻處理結果來消除「中間結果」變數
- 減小每個變數的作用域,越小越好。把變數移到一個有最少程式碼可以看到它的地方。眼不見,心不煩
- 只寫一次的變數更好。那些只設置一次值的變數(或者const、final、常量)使得程式碼更容易理解。
第三部分 重新組織程式碼
我們會講到三種組織程式碼的方法:
- 抽取出那些與程式主要目的「不相關的子問題」
- 重新組織程式碼使它一次只做一件事情
- 先用自然語言描述程式碼,然後用這個描述來幫助你找到更整潔的解決方案
第10章 抽取不相關的子問題
-
所謂工程學就是關於把大問題拆分成小問題再把這些問題的解決方案放回一起。把這條原則應用於程式碼會使程式碼更健壯並且更容易讀。
- 積極地發現並抽取出不相關的子邏輯
- 純工具程式碼
- 通常來講,如果你在想:「我希望我們的庫里有XYZ()函數」,那麼就寫一個!(如果它還不存在的話)經過一段時間,你會建立起一組不錯的工具程式碼,後者可以應用於多個項目。
- 其他多用途程式碼
- 當format_pretty()中的程式碼自成一體後改進它變得更容易。當你在使用一個獨立的小函數時,感覺添加功能、改進可讀性、處理邊界情況等都更容易。
- 創建大量通用程式碼
- 項目專有的功能
- 簡化已有介面
- 按需重塑介面
- 過猶不及
- 引入這麼多小函數實際上對可讀性是不利的,因為讀者要關注更多東西,並且按照執行的路徑需要跳來跳去
- 純工具程式碼
第11章 一次只做一件事
- 應該把程式碼組織得一次只做一件事情。
第12章 把想法變成程式碼
- 用自然語言描述程式然後用這個描述來幫助你寫出更自然的程式碼。這個技巧出人意料地簡單,但很強大。看到你在描述中所用的詞和短語還可以幫助你發現哪些子問題可以拆分出來。 但是這個「用自然語言說事情」的過程不僅可以用於寫程式碼。
第13章 少寫程式碼
- 不是所有的程式都需要運行得快,100%準確,並且能處理所有的輸入。如果你真的仔細檢查你的需求,有時你可以把它削減成一個簡單的問題,只需要較少的程式碼。
- 我們所描述的是宇宙的自然法則——隨著任何坐標系統的增長,把它粘合在一起所需的複雜度增長得更快。 最好的解決辦法就是「讓你的程式碼庫越小,越輕量級越好」,就算你的項目在增長。那麼你就要:
- 創建越多越好的「工具」程式碼來減少重複程式碼(見第10章)
- 減少無用程式碼或沒有用的功能
- 讓你的項目保持分開的子項目狀態
- 總的來說,要小心程式碼的「重量」。讓它保持又輕又靈。
- 熟悉你周邊的庫
- 很多時候,程式設計師就是不知道現有的庫可以解決他們的問題。或者有時,它們忘了庫可以做什麼。知道你的庫能做什麼以便你可以使用它,這一點很重要。
我的總結
在日常開發中,比較注重編碼規範及命名等細節,個人認為命名和注釋寫得好是需要觀察、積累和總結的,這也很重要,同時在閱讀jdk、spring、mybatis等優秀框架源碼也發現好的命名的重要性,下面總結了一些命名及編碼方式,周知的駝峰命名、常量大寫等就不列舉了。只寫一些書中沒提到的。
命名相關
-
動賓格式命名方法
- prepareContext、prepareEnvironment
-
前綴命名
- spring中doXXX是真正做事情的方法,如doLoadBeanDefinitions、doRegisterBeanDefinitions
- preXXX、postXXX:前置、後置處理方法
- loadXXX:loadBeanDefinitions
-
後綴命名
- XXXListener:一看就知道是監聽器
- XXXFactory、XXXDelegate、XXXTemplate:使用了工廠模式、委派模式、模板模式等
-
善用詞性
- listeners.starting()、listeners.started(context)等不用說就知道區別,表示不同階段
-
名詞單複數
- user、users:分別代表一個用戶和多個用戶
程式碼風格
- 按功能劃分程式碼,一個類不超過千行
- 常量統一寫在頭部
- 多次使用的抽象成工具類
-
語義化、函數式編程
也歡迎關注我的公眾號:yizhuxiaozhan,二維碼: