《編寫可讀程式碼的藝術》第7章 簡化迴圈和邏輯
阿新 • • 發佈:2021-01-07
1. 條件語句中引數的順序
左邊更傾向於使用變化的,右邊更傾向於使用穩定的。
1 if (length >= 10) 2 // or 3 if (10 <= length) 4 5 while (bytes_received < bytes_expected) 6 //or 7 while (bytes_expected > bytes_received)
1 //以前的慣例 2 if (obj = NULL) // 易產生bug 3 if (NULL == obj) // 可以預防bug 4 5 if (obj = NULL) // 現代編譯器會給出警告 6 //test.cpp:185:11: 警告:建議在用作真值的賦值語句前後加上括號 [-Wparentheses]7 // 185 | if (obj = NULL) 8 9 if ((obj = NULL)) // OK 10 if (obj == NULL) // OK
2. if / else 語句塊的順序
先處理正邏輯
1 if (!url.HasQueryParameter("expand_all")) { 2 response.Render(items); 3 } else { 4 for (int i = 0; i < items.size(); i++) { 5 items[i].Expand(); 6 } 7 } 89 //當說“不要去想粉紅色的大象”時,不要就被“想粉紅色的大象”淹沒了 10 // 所以正向邏輯放在前面更易讀 11 if (url.HasQueryParameter("expand_all")) { 12 for (int i = 0; i < items.size(); i++) { 13 items[i].Expand(); 14 } 15 } else { 16 response.Render(items); 17 }
負邏輯更簡單/更危險/更有趣時,可以放在前面
1 if not file: 2 # Log the error ...3 else: 4 # ...
3. ?:表示式(又名“三目運算子”)
富有爭議的表示式
1 // 緊湊 2 time_str += (hour >= 12) ? "pm" : "am"; 3 4 // 冗長 5 if (hour >= 12) { 6 time_str += "pm"; 7 } else { 8 time_str += "am"; 9 }
然而這種表示式很有可能變得很難讀
1 return exponent >= 0 ? mantissa * (1 << exponent) : mantissa / (1 << -exponent);
要使理解的難度最小化,因此建議只在最簡單的情況下使用?:(“三目運算子”)
4. 避免使用do/while迴圈
do/while奇怪之處在於:條件放在其“保護”的程式碼之後。讀者需要讀兩遍程式碼,很不自然。
大多數do/while迴圈可以改成 while迴圈
1 //但是不要這樣做 2 body 3 while (condition) { 4 body (again) 5 }
5. 從函式提前返回
”保護語句“在函式開頭進行檢查
6. 臭名昭著的goto語句
大部分時候需要避免使用goto語句。但是下面的情況除外:cleanup可以避免大段的重複程式碼。
1 if (p == NULL) goto cleanup; 2 //... 3 cleanup: 4 fclose(file1); 5 fclose(file2); 6 //... 7 return;
7. 最小化巢狀
巢狀很深的程式碼很難理解。看上去很簡單的改動可能會使巢狀越來越深。
因此,當你對程式碼進行改動時,從全新的角度審視它,把它當成一個整體看待。
8. 通過提早返回來減少巢狀
9. 減少迴圈內巢狀,善用continue。
10. 你能理解執行的流程嗎?
執行緒 / 訊號量 / 中斷處理程式 / 異常 / 函式指標和匿名函式 / 虛方法
上面這些幕後執行的程式碼很有用,甚至可以讓程式碼冗餘更少,更具可讀性。
但是用的太多會令人難以理解,bug也更難跟蹤。不要讓程式碼中這些部分比例太高。
cleanup