《編寫可讀代碼的藝術》讀書筆記
前言
本書旨在協助你把代碼寫的更好
第1章 代碼應當易於理解
是什麼讓代碼變得“更好”
可讀性基本定理
代碼的寫法應當使別人理解它所需要的時間最小化
總是越小越好嗎
把理解代碼所需的時間最小化是一個更好的目標
理解代碼所需的時間是否與其他目標有衝突
根本不會相互影響
最難的部分
經常想其他人是否會覺得你的代碼容易理解,需要額外的時間
第一部分 表面層次的改進
選擇好的名字、寫好的注釋以及把代碼整潔地寫成更好的格式
第2章 把資訊裝到名字裡
選擇專業的詞
術語上區分它的職能、找到更有表現力的詞
避免像tmp和reveal這樣泛泛的名字
好的名字應當描述變數的目的或者它所承載的值
如果你需要使用像tmp、lt或者retval這樣空泛的名字,那麼你要有個好的理由
把具體的名字帶替抽象的名字
在給變數、函數或者其他元素命名時,要把它描述的更具體而不是更抽象
為名字附帶更多資訊
如果關於一個變數有什麼重要事情要讀者必學知道,那麼是值得把額外的“詞”添加到名字中的。
帶單位的值、附加其他重要屬性
名字應該有多長
名字不能太長
在小的範圍裡可以使用短的名字
輸入長名字————不再是個問題(編輯器的“單詞補全”功能)
首字母縮減詞和縮寫
丟掉沒用的詞
利用名字的格式來傳遞含義
駝峰類名、底線方法名、常量駝峰加kConstant首碼、宏是全大寫底線分詞、建構函式首字母大寫、jQuery對象前加$
有目的地使用大小寫、底線等
第3章 不會誤解的名字
要多問自己幾遍:“這個名字會被別人解讀成其他的含義嗎?”
例子:Filter()
二義性詞,不知道其含義是“挑出”還是“減掉”
例子:Clip(text,length)
二義性詞,可能是從尾部刪除,也可能時裁掉最大長度為length的一段
推薦用first和last來表示包含的範圍
命名極限最清楚的方式是在要限制的東西前加上max或者min。
推薦用begin和end來表示包含/排除範圍
給布爾值命名
當為布爾變數或者返回布爾值的函數選擇名字時,要確保返回true和false的意義很明確。
最好避免使用反義名字。如'disable_ssl'。
與使用者的期望匹配
有些名字之所以會讓人誤解是因為使用者對它們的含義有先入為主的印象,就算你的本意並非如此。在這種情況下,最好放棄這個名字而改用一個不會讓人誤解的名字。
例子:如何權衡多個備選名字
分析每個備選名字,考慮各種讓人誤解的可能性。
第4章 審美
讓代碼變得更易讀有三條原則:
. 使用一致的布局,讓讀者很快就習慣這種風格. 讓相似的代碼看上去相似. 把相關的程式碼分組,形成代碼塊
為什麼審美這麼重要
讓人愉悅的代碼更容易閱讀
重新安排換行帶來保持一致和緊湊
用方法來整理不規則的東西
使代碼“看上去漂亮”通常會帶來不限於表面層次的改進,它可能會幫你把代碼的結構做的更好。
在需要時使用列對齊
選一個有意義的順序,始終一致地使用它
有一大堆get參數要擷取,我們對代碼中每個擷取的順序有一些想法:
. 讓變數的順序與對應的HTML表單中欄位的順序相匹配. 從“最重要”到“最不重要”. 按字母順序排序
第5章 改寫什麼樣的注釋
注釋的目的是盡量協助讀者瞭解得和作者一樣多
什麼不需要注釋
不要為那些從代碼本身就能快速推斷的事實寫注釋
不要為了注釋而注釋
不要給不好的名字加註釋————應該把名字改好
記錄你的思想
寫代碼時有過重要想法
站在讀者的角度
想象你的代碼對於外人來講看起來是什麼樣子的
公布可能的陷阱
全域觀注釋,考慮到新人加入,讓她熟悉程式碼程式庫
最後的思考————克服“作者心理阻滯”
第6章 寫出言簡意賅的注釋
注釋應當有很高的資訊/空間率
讓注釋保持緊湊
避免使用不明確的代詞
如it、this
潤色粗糙的句子
精確的描述函數的行為
Return the number of lines in this file和Count how many newline bytes('\n') are in the file
用輸入/輸出例子來說明特別的情況
Example
聲明代碼的意圖
What you want do
“具名函數參數”的注釋
預設參數 Connetc(timeout = 10, user_encryption = false)
做不到,用Connet(/* timeout_ms= */ 10, /* use_encryption = */ false)
採用資訊含量高的詞
如果你感覺到一段注釋太長了,那麼可以看看是不是可以用一個典型的編程情境來描述它
第二部分 簡化迴圈和邏輯
試著最小化代碼中的“思維包袱”
第7章 把控制流程變的易讀
把條件、迴圈以及其他對控制流程的改變做得越“自然”越好。運用一種方式使讀者不用停下來重讀你的代碼
條件陳述式中參考的順序
比較的左側比較的右側“被問詢的”運算式,它更傾向於不斷變化的用來做比較的的運算式,它的值更傾向於常量
if/else語句塊的順序
. 首先處理正邏輯而不是負邏輯的情況。例如,用if(debug)而不是if(!debug)。. 先處理掉簡單的情況。這種方式可能還會使if和else在螢幕之內都可見. 先處理有趣的或者是可疑的情況
?:條件運算式(又名“三目運算式”)
相對於追求最小化程式碼數,一個更好的度量方法是最小化人們理解它所需的時間。
預設情況下使用if/else。三目運算子?:只有在最簡單的情況下使用
避免do/while迴圈
我的經驗是,do語句是錯誤和困惑的來源……我傾向於把條件放在“前面我能看到的地方”。其結果是,我傾向避免使用do語句。
從函數中提前返回
臭名昭著的goto
應該避免使用goto
最小化嵌套
嵌套很深的代碼很難以理解
當你對代碼做改動時,從全新的角度審視它,把它作為一個整體來看待
通過提早返回來減少嵌套
你能理解執行的流程嗎?
不要讓代碼中使用“線程”、“訊號量”、“異常”、“函數指標和匿名函數”、“虛方法”過多讓實現流程變的進階難懂。
第8章 拆分超長的運算式
把你的超長運算式拆分成更容易理解的小塊
用做解釋的變數
臨時中間變數
總結變數
使用德摩根定理
分別取反,轉換與/或
濫用短路邏輯
要小心“智能”的小代碼————它們往往以後會讓別人讀起來感到困惑。
例子:與複雜的邏輯戰鬥
struct Range{ int begin; int end; //For example,[0,5) overlaps with [3,8) bool OverlapsWith(Range other);}
相反比較
bool Range::OverlapsWith(Range other){ if(other.end <= begin) return false; if(other.begin >= end) return false; return true;}
拆分巨大的語句
提取公用部分放入函數作為總結變數
另一個簡化運算式的創意方法
定義宏
第9章 變數與可讀性
變數越多,就越難全部跟蹤它們的動向
變數的範圍越大,就更需要 跟蹤它的動向越久
變數改變得越頻繁,就越難以跟蹤它的當前值
減少變數
沒有價值的臨時變數
減少中間結果
減少控制流程變數
縮小變數的範圍
讓你的變數對盡量少的程式碼可見。
唯寫一次的變數更好
操作一個變數的地方越多,越難確定它的當前值。
最後的例子
第三部分 重新組織代碼
函數層級對代碼做的更大的改動
第10章 抽取不相關的子問題
看看某個函數或者代碼塊,問問自己:這段代碼高層次的目標是什嗎?
對於每一行代碼,問一下:它是直接為了目標而工作嗎?這段代碼高層次的目標是什麼呢?
如果足夠的函數在解決不相關的子問題,抽取代碼到獨立的函數中。
把一般代碼和項目專有的代碼分開
介紹性的例子:findClosestLocation()
純工具代碼
其他多用途代碼
子代碼自稱一體後改進它變得更容易
建立大量通用代碼
通用代碼很好,因為“它完全地從項目的其他部分中解耦出來”
項目專有的功能
簡化已有介面
你永遠都不要安於使用不理想的介面
按需重塑介面
過猶不及
別分的太細,多個小函數對可讀性不利
第11章 一次只做一件事
應把程式碼群組織得一次製作一件事情。
任務可以很小
投票的例子
從對象中抽取值
更大型的例子
第12章 把想法變成代碼
如果你不能把一件事情解釋給你祖母聽的話說明你還沒有真正理解它。
清楚地描述邏輯
用自然語言描述邏輯
瞭解函數庫是有協助的
編寫精鍊代碼的一部分工作是瞭解你的庫提供了什麼。
把這個方法應用於更大的問題
自然語言描述邏輯、適當遞迴調用自身
第13章 少些代碼
最好讀的代碼就是沒有代碼
別費神實現那個功能————你不會需要它
質疑和拆分你的需求
保持小程式碼程式庫
讓你的程式碼程式庫越小,越輕量級越好
刪除沒用的代碼
熟悉你周邊的庫
每隔一段時間,花15分鐘閱讀標準庫中的所有函數/模組/類型的名字
成熟的庫中,每行代碼都代表大量的設計、調試、重寫、文檔、最佳化和測試。
例子:使用Unix工具而非編寫代碼
通過以下方法避免編寫新代碼:
. 從項目中消除不必要的功能,不要過度設計。
. 重新考慮需求,解決版本最簡單的問題,只要能完成工作就行。
. 經常性地通讀標準庫的整個API,保持對它們的熟悉程度。
第四部分 精選話題
第14章 測試與可讀性
使測試易於閱讀和維護
測試應當具有可讀性,以便其他程式員可以舒服地改變或者增加測試
這段測試什麼地方不對
使這個測試更可讀
對使用者隱去不重要的細節,以便更重要的細節會更突出
建立最小的測試聲明
大多數測試的基本內容都能精鍊成“對於這樣的輸入/輸出情形,期望有這樣的行為/輸出”
實現定製的“微語言”
讓錯誤訊息具有可讀性
瞭解顯示錯誤的庫
手工打造錯誤訊息
錯誤訊息應當越有協助越好
選擇好的測試輸入
基本原則是,你應當選擇一組最簡單的輸入,它能完整地使用被測代碼
又簡單又能完成工作的測試值更好
一個功能的多個測試
為測試函數命名
Test__()
那個測試有什麼地方不對
對測試較好地開發方式
你會開始把代碼寫得容易測試!
走得太遠
. 犧牲真實代碼的可讀性,只是為了使能測試. 著迷於100%的測試覆蓋率. 讓測試成為產品開發的阻礙
第15章 設計並改進“分鐘/小時計數器”
問題
跟蹤過去一分鐘和一個小時裡Web伺服器傳輸了多少位元組。
定義類介面
嘗試1:一個幼稚的方案
嘗試2:傳送帶設計方案
嘗試3:時間桶設計方案
比較三種方案