易讀程式之美學:提升程式碼可讀性的簡單法則 | 維持健康的好方法 - 2024年5月

易讀程式之美學:提升程式碼可讀性的簡單法則

作者:Dustin Boswell、Trevor Foucher
出版社:歐萊禮
出版日期:2013年04月30日
ISBN:9789862767191
語言:繁體中文
售價:432元

  本書主題是介紹如何寫出有高度可讀性的程式碼,主要概念是程式碼必需易於理解,更精 確地說,撰寫程式碼時應將他人理解程式碼所需的時間縮到最短。
  本書使用許多不同程式語言的範例解釋這個概念,包含了 C++、Python、JavaScript 以及 Java;避開了程式語言的進階特性,即使不熟悉這些程式語言的讀者,也能夠輕易理解內 容(畢竟,可讀性概念大多與程式語言本身無關)。
  本書各章節由程式設計的不同層面說明如何讓程式「更易於理解」,共分為四個部份;
  表層改善  名稱、註解與美學:使用在每一行程式碼的簡單技巧。
  簡化迴圈與邏輯  透過改善程式的迴圈、邏輯與變數,提高可讀性的方法。
  重新組織程式碼  組織大區塊程式以及從函數層面解決問題的高階作法。
  精選主題  將「易於理解」原則應用在測試以及大型資料結構的程式。
作者簡介
Dustin Boswell
  畢業於 CalTech,任職於 Google 五年,負責網頁抓取基礎設施(web crawling infrastructure)以及廣告相關程式。他建立了多個網站,喜歡海量資料與機器學習等領域。
Trevor Foucher
  曾任職於微軟十餘年,擔任 Windows 與安全相關產品的工程師、經理以及技術負責人,目前任職於 Google 負責廣告程式與搜尋基礎設施。

前言
1 程式碼應該易於理解 「更好」的意義? 可讀性基本定理 比較短的程式都比較好嗎? 最短理解所需時間與其他目標是否衝突 困難所在
第一部份 表層改善
2 富含資訊的名稱 選擇詞彙 避免 tmp 與 retval 之類的通用名稱 優先使用具體名稱而非抽象名稱 在名稱中加入額外資訊 名稱該有多長? 利用名稱格式加入更多意義 結語
3 不被誤解的名稱 範例:Filter() 範例:Clip(text, length) 包含邊界的極值優先使用 min 與 max 閉區間優先使用 first 與 last 半開放開區間優先使用 begin 與 end 布林值名稱 符合使用者的預期 範例:評估多個可用名稱 結語
4 美學 美學為何重要? 調整斷行讓程式更加一致與簡潔 使用方法(method)消除混亂 適當使用列對齊 選擇有意義的順序並堅守到底 將宣告組織成區塊 區分程式碼「段落」 個人風格與一致性 結語
5 認識註解 不該註解的部份 記錄自己的想法 為讀者設想 最後 - 避免作者區塊 結語
6 讓註解精確與簡潔 維持註解簡潔 避免模稜兩可的代名詞 修整草率的語句 精確描述函數行為 使用具代表性的輸入∕輸出範例 表達程式意圖 「函數參數名稱」的註解 使用訊息密集的詞彙 結語
第二部份 簡化迴圈與邏輯
7 提高控制流程可讀性 條件式中的條件順序 if/else 區塊順序 □: 條件式(也稱為「三元運算子」) 避免 do/while 迴圈 儘早由函數中返回 惡名昭彰的 goto 減少巢狀結構 能否理解執行流程? 結語
8 分解巨大表示式 解釋性變數 摘要變數 利用笛摩根定律 誤用捷徑邏輯 範例:與複雜邏輯搏鬥 分解巨大的敘述 另一個有創意的簡化手法 結語
9 變數與可讀性 消除變數 縮限變數的範圍(scope) 偏好單次寫入的變數 最後的範例 結語
第三部份 重新組織程式碼
10 抽離不相關子問題 說明範例:findClosestLocation() 純工具程式碼 其他通用程式碼 建立大量通用程式碼 專案專屬功能 簡化既有介面 依需求重塑介面 過猶不及 結語
11 一次一項工作 工作可以很小 從物件抽取數值 較大的範例 結語
12 將想法轉化為程式碼 清述描述邏輯 認識函式庫能提供的協助 在較大問題應用本方法 結語
13 撰寫較少程式碼 不開發那些功能 - 不會需要 詢問與分解需求 維持程式碼小而美 熟悉使用的函式庫 範例:用 Unix 工具代替撰寫程式 結語
第四部份 精選主題
14 測試與可讀性 讓測試易讀與維護 這些測試有何問題? 讓測試更易讀 讓錯誤訊息易讀 選擇良好的測試輸入 測試函數的命名 那些測試有何問題? 測試友善的開發 過度應用本原則 結語
15 「分∕時計數器」的設計與實作 問題說明 定義類別介面
第一次嚐試:粗略的解決方案 第二次嚐試:輸送帶式設計 第三次嚐試:時間區段(Time-Bucketed)設計 比較三種解決方案 結語
A 延伸閱讀
索引

前言
  我們曾任職於十分成功的軟體公司,和許多傑出工程師一同工作,但仍然遇到許多需要改 善的程式碼,事實上,我們看過許多醜陋的程式碼,讀者可能也是如此。
  漂亮的程式碼有啟發性,能快速地讓讀者瞭解情況,使用起來也十分有趣,還能激發程式 設計師改善所寫的程式碼。
  本書目標是協助讀者改善程式碼,「程式碼」是指讀者從編輯器中看到的每一行程式而非 專案的整體架構,也不是指設計模式(design pattern)。這些雖然十分重要,但在我們的 經驗裡,程式設計師大多數的時間是花費在「基本」工作上:變數命名、迴圈以及各式各 樣函數層級的問題。這些大多需要閱讀與編輯原有的程式碼,希望本書能對讀者日常的程 式設計工作有所助益,願意推薦給團隊其他成員。

第一章程式碼應該易於理解過去五年,作者們收集了數百個「不良程式碼」(大多數是自己寫的),分析造成不良的原 因,找出改善的原則與技巧,最後發現所有的原則都回溯到相同的概念。重要概念程式碼應該易於理解。作者們相信這是寫程式時最重要的原則,接下來的內容,會將這個原則應用在程式設計師 每天工作的各個層面,首先詳細說明這個原則以及最為重要的原因。「更好」的意義? 大多數程式設計師(包含作者在內)根據直覺選擇呈現程式碼的方式,大家都知道(即使 下面範例中的程式碼有完全相同的行為),但這種寫法:for (Node * node = list->head; node != NULL; node = node->next) Print(node->data);比起以下的寫法來得好:Node* node = list->head; if (node == NULL) return;while (node->next != NULL) { Print(node->data);node = node->next;}if (node != NULL) Print(node->data);但很多時候,決定何者要好並不容易,例如這段程式:return exponent >= 0 ? mantissa * (1 << exponent) : mantissa / (1 << -exponent);是否比這種寫法來得好:if (exponent >= 0) {return mantissa * (1<< exponent);} else {return mantissa / (1 << -exponent);}第一個版本比較簡潔,第二個版本則較容易瞭解,那個條件比較重要?一般來說,該如何 決定程式碼的呈現方式?可讀性基本定理 看了許許多多的範例之後,得到的結論是,存在一個最為重要的可讀性指標,重要到能夠 將之稱為「可讀性基本定理」。重要概念撰寫程式時應該將讀者理解所需的時間降到最短。這是什麼意思?很簡單,找個一般的能力的同事,測量他們理解程式碼需要的時間,這就 是撰寫程式時希望縮短的理論值。「理解」的要求非常高,完全理解 程式碼,應該要能夠修改、找出臭蟲並知道與程式其 他部份互動的方式。現在,讀者可能認為,「誰會在乎其他人懂不懂?我是唯一一個使用這些程式碼的 人!」即使單人專案也值得追求這個目標,「其他人」可能是六個月後的自己,到時候對 程式碼不再熟悉。永遠無法確定 ── 可能有其他人加入專案,或是「被丟棄的程式碼」可能被用在其他專案當中。比較短的程式都比較好嗎?一般來說,解決問題所需的程式碼愈短愈好(參看第十三章,撰寫較少程式碼),瞭解一 個由 2000 行程式碼的類別所需時間可能比由 5000 行程式碼的類別來得短。但較少的程式碼並非總是比較好!很多時候像這樣的單行表示式:assert((!(bucket = FindBucket(key))) || !bucket->IsOccupied())比起兩行的寫法需要更多時間理解:bucket = FindBucket(key)if (bucket != NULL) assert(!bucket->IsOccupied());同樣的,註解雖然會在檔案中「加入更多程式」,卻能讓人更快速的理解程式碼:// hash = (65599 * hash) +c 的快速版hash = (hash << 6) + (hash << 16) - hash + c;雖然減少程式碼數量是個很好的目標,但縮短理解時間更加重要。最短理解時間是否與其他目標衝突? 讀者可能會想「其他的條件怎麼辦?像是程式效率、良好的架構或是可測試性等等?這些 條件難道不會和讓程式易於瞭解的原則衝突嗎?」作者發現其他目標並不會受到太大的影響,即使在高度最佳化的程式碼中,仍然有辦法讓 程式碼具有高度可讀性;實際上讓程式碼易於瞭解常常也會產生良好的架構且易於測試。本書接下來將介紹如何在各種情況應用「易於閱讀」,切記,難以決定時,可讀性基本定 理優於本書所提的其他任何原則。同時,某些程式設計師無法忍受任何不夠完美的程式 碼,這時必須停下腳步,想想 程式碼是否易於理解?」如果已經如此,也許可以放心處理其他的程式碼。困難所在 的確,時時考慮某個虛擬讀者是否能夠理解程式碼需要花費不少額外的功夫,需要將大腦 切換到撰寫程式時沒有用到的部份才行。但如果(和作者一樣)選擇了這個目標,可以確定讀者將會成為更好的程式設計師,產 生更少的臭蟲,對成品更有認同感,同時能產出其他人樂於使用的程式碼,就讓我們開 始吧!第五章認識註解本章介紹程式碼中的註解,許多人認為註解只是「解釋程式碼行為」,但那只是其中一小 部份。重要概念註解的目的是協助使用者瞭解程式碼作者的思想。撰寫程式時,程式設計師腦中有許多寶貴的資訊;其他人閱讀程式碼時少了這些資訊── 只剩下眼前的程式碼。本章提供許多範例,說明何時該寫下腦中的資訊,略過一般的註解,集中在更為有趣以及 註解「較為不足」的部份。本章內容組織為以下幾個部份:z 瞭解不該註解的部份。z 記錄撰寫程式時的想法。z 為讀者設身處地,想像讀者需要知道的東西。不該註解的部份閱讀註解會佔用閱讀程式碼的時間,註解也會佔用畫面上的空間,因此註解必須有價值; 該如何區分有價值的註解與沒必要的註解?下列程式中所有的註解都沒有必要:// Account 的類別定義class Account { public:// 建構子Account();// 將 profit 成員設為新值void SetProfit(double profit);// 傳回這個 Account 的 profitdouble GetProfit();};這些註解既沒有提供任何額外的資訊,也無法讓人進一步瞭解程式碼,所以沒有必要。


相關書籍