Book - 易讀程式之美學-提升程式碼可讀性的簡單法則 (The Art of Readable Code)

Posted on 2024-03-05

昨天有分享了 高效程序員的 45 個習慣 (Practices of an Agile Developer:Working in the Real World) 這本書,今天來分享另外一本書

易讀程式之美學-提升程式碼可讀性的簡單法則 (The Art of Readable Code) 這本書,我覺得比 無瑕的程式碼 (Clean Code) 來的輕薄容易看,可以看成是一本前導書

文章內容,是在之前公司內,我舉辦的讀書會,我整理的一些內容,基本上都是我要特別點出來跟大家分享的重點。

第一章 程式碼應該易於理解

  • 可讀性基本原則
    • 應該將讀者理解所需的時間降到最短 -> 呈現「意圖」
    • 1 個月後的自己有沒有辦法看的懂
  • 短的程式碼一定是最好的嗎 ?

第二章 富含資訊的名稱

  • 名稱可視為簡短的註解
    • 命名好的話,還需要註解嗎 ?
  • 非英語系國家
    • domain 而來

01 選擇詞彙

  • 根據上下文 fetch 可能會比 get 好
    • 基本上 java 是不加 get 的
  • size -> 什麼的 size ?

02 避免 tmp 與 result 通用名稱

  • data / result
  • tmp ? 什麼 tmp ?
    • xxxtmp
  • 有意義的使用 for 的 i
    • 視情況而定

03 優先使用具體名稱而非抽象名稱

  • server can start -> can listen on port

04 在名稱中加入額外資訊

  • 把單位加到變數上
    • file size -> size_mb
    • delay -> delay second
  • 加入其它重要屬性
    • html_utf8
    • plaintext_password
  • 匈牙利命名 ?
    • 類型加在最前面

05 名稱該有多長

  • 剛剛好就好
  • 讓 ide 協助你完成
  • 縮寫不要縮的讓它媽媽都不認的

06 利用名稱格式加入更多意義

  • 不同語言的規定不同

第三章 不被誤解的名稱

  • 英文的 right ?
    • 英文的單子很多不同的時候有正/反的意思
  • think aloud 放聲思考法
    • 是在進行某種認知任務時,該人士會公開地表達出他們正在想什麼,以便其他人可以聽到他們的思考過程
  • filter => 無法判斷是 選取 select 還是 排除 exclude

邊界的極值 min, max

  • 每個人對於 limit 的定義不同,包不包含邊界值

封閉區間 first, last

  • 起始點 ~ 包含終點

半開放區間 begin, end

  • 起始點 ~ 不包含終點

bool 名稱

  • is, has, can, should

符合使用者的預期

  • get
    • 以 c# 的 prop get 為例
    • 是否應該在裡面存取 db
  • size
    • 以 c# 的 count 為例
    • api 的熟悉

評估多個可用名稱

  • 先選出名稱再來討論

第四章 美學

  • 排版,固定的模式
  • 類似的程式碼有相似外觀
  • 程式碼變成段落

調整斷行讓程式更加一致與簡潔

  • 類似的程式碼,排版應該要類似

用方法消除混亂

  • 測試重構
    • 消除重複
    • 呈現意圖

適當使用列對齊

  • 個人不建議

選擇有意義的順序並堅守到底

  • 對應 html input
  • 重要性
  • 字母

上下 (前面) 要一致就好

將宣告組織成區塊

  • interface 同樣功能的 method 放在同一個區塊
    • 可以看到一些 code smell
      • 拆分

區分程式碼「段落」

  • 區分不同的段落
    • 寫文章一樣的感覺
  • 先寫註解在寫程式

個人風格與一致性

一致的風格比正確的風格更為重要

第五章 認識註解

  • 註解的目的,是協助使用者了解作者的思想
    • 重點在於記錄 why

不該註解的部份

  • 一看就知道的事情就不用註解
  • 不要為了註解而註解
  • 不要註解不好的名稱 - 直接修正名稱
  • 好程式 > 壞程式 + 好註解

記錄自己的想法

  • 記錄 why
  • 註解程式碼 bug
    • todo 的意義
  • 常數的註解
    • 視情況而定要不要加
      • 永遠固定的值,基本上不用

為讀者設想

  • 想像程式碼在外人眼中的樣子
  • 預期可能出現的問題
    • 可能是反模式,為什麼不修改可以表達 ?
  • 註明可能出現的陷阱
    • 與其說是陷阱,不如說是限制
      • 例如寄信附檔不可超過多大
  • 全局註解
    • 向團隊的新成員,介紹程式的來龍去脈
      • 我覺得是文件,而不是註解
  • 摘要註解
    • 跟前面說的很類似,先註解再寫程式

避免寫作抗拒

  • 步驟
    • 寫下當時心中的想法
    • 讀出註解,看看是否需要修正
    • 改善

第六章 讓註解精確與簡潔

避免模稜兩可的代名詞

  • 它是誰 ?

使用具代表性的輸入 / 輸出範列

  • 註解加上範列
  • 測試 ?

函數參數名稱的註解

  • ide 有提供的功能
  • c# 的具名參數

使用訊息密集的詞彙

  • 大家都知道的 ?
    • 工程師 - 知識的詛咒
  • domain 通用語言
    • 工程師通用語言 - design pattern
  • 第一個部份在表層,重點在於「表現意圖」(可讀性)

第七章 提高控制流程可讀性

01 條件式中的條件順序

  • if 的右邊大多都是固定的值
  • 尤達表示法 ?
    • 有人習慣會把 null 寫在左邊

02 if/else 區塊順序

  • 先正向再反向
  • 把程式碼拆出括號外

03 ?: 三元運算式

  • 視情況使用
  • 最多不要超過兩層的三元運算式

04 避免 do/while 迴圈

  • do/while 因為不好閱讀,應該要改成 while
  • 個人覺得可以不用就不用,避免無窮迴圈

05 儘早由函數中返回

  • early return
  • 衛語句 (guard clause)
  • 防禦性程式設計 (defensive programming)

06 惡名昭彰的 goto

  • 可以用,但不要用

07 減少巢狀結構

  • 就拆掉括號
  • early return 是最基本的方式
  • return 變 continue 也是一招
  • 使用 linq

08 能否理解執行流程

  • 不要用 exception 控制流程

第八章 分解巨大表示式

01 解釋性變數

  • 使用「變數」達到比原本更有意義
    • 雖然會造成 temp variable

02 摘要變數

  • 跟解釋有點像
  • 是不是會違反 feature envy ?

03 利用笛摩根定律

  • 利用 ide 的提示
  • 之前提過的正向表示

04 誤用捷徑邏輯

  • 簡短不一定是好的
  • 可以拆開 if 內的判斷式
  • us falsely value

06 分解巨大的敘述

  • jQuery 常用的重構手法
    • 消除重複

07 另一個有創意的簡化手法

  • 跟上一個有點像,重點是怎麼找到重複

第九章 變數與可讀性

01 消除變數

  • temporary variable
  • 中間結果 variable 有時還是需要的
    • 可以利用程式碼的特性 yield return

02 縮限變數的範圍

  • 全域變數
    • js 就比較麻煩,新版本多加了宣告的方式 let , const
    • python 也是
  • 可以是區域就設定成區域
    • 神奇 field
  • 離使用的地方愈近愈好
    • 副作業比較小

03 偏好單次寫入的變數

  • const
  • readonly static
  • 第二部份是針對某個區域,第三部份是針對大範圍

第十章 抽離不相關子問題

  • 程式主流程的目的為何
  • 是不是有解決主要目的不相關的子問題
  • 將子問題抽成獨立的 method/ function

01 說明範例

  • 子問題
    • 可以提供單一完整功能,不在乎其它人怎麼使用它

02 純工具程式碼

  • 希望語言本身可以提供的函數
  • static helper ~

03 其它通用程式碼

  • 非 02 的工具程式碼 ?

04 建立大量通用程式碼

  • 拉出去變成 lib/ package

05 專案專屬功能

  • 是不是跟 domain 無關的 helper ?

06 簡化既有介面

  • 永遠不需要屈就於不夠理想的介面
  • 在原本的 method 上面多包一層
    • 比較方便使用
    • ToJoinString()

07 依需求重塑介面

  • glue 膠合程式碼
    • 程式碼與程式本身邏輯無關 -> 05 的意思

08 過猶不及

  • method 拆分過小
    • 等有人用的時候再拆就好

第十一章 一次一項工作

  • 程式碼重組
  • 函數應該只作一件事
    • SRP
  • 這裡主要是針對函數,並沒有拆 class

01 工作可以很小

  • 原本
    • 投票轉數字 + 更新
  • 之後
    • 投票轉數字
    • 更新

02 從物件抽取數值

  • || js 的特性
  • TDD 也是在簡化操作

結語

  • 列出執行的工作
  • 工作可以抽離為函數 / 類別
  • 真正困難 - 列出所有函數執行的小工作

第十二章 將想法轉化為程式碼

  • 怎麼好的跟別人解釋

01 清楚描述邏輯

  • 先口語說邏輯 -> 有點類似之前說的寫註解
  • 不看程式碼說要做的事情

02 認識函數庫能提供的協助

  • 重點在於對程式本身提供的 api / sdk 要熟悉
    • 之前有說過的 join string 的例子

03 應用在較大的問題

  • 如果原本已經有程式碼
    • 如何重構
  • 如果是新功能
    • 如何規劃設計

04 結語

第十三章 撰寫較少的程式碼

01 不開發那些功能 - 不會需要

  • 80 / 20 法則
  • 那些是真正核心的功能

02 詢問與分解需求

  • 了解功能背後的 why
    • 重點在於解決問題,而不是功能

03 維持程式碼小而美

  • 重用程式碼
  • 移除沒有使用的程式碼
  • 拆專案 ?

04 熟悉使用的函式庫

  • 跟前一章的「02 認識函數庫能提供的協助」一樣意思

05 範例

  • 重點在於善用工具
    • 役物而不役於物

第十四章 測試與可讀性

01 讓測試易讀與維護

  • 測試程式,也是程式,也是需要重構和維護

03 讓測試更易讀

  • 隱藏不重要的細節,突顯重要的細節
  • 「第十二章 將想法轉化為程式碼」
    • 讓測試程式跟說話一樣
  • input / output 一行有點跨張了點

04 讓錯誤訊息易讀

  • 測試應該可以反應錯誤的情境
    • self valid

05 選擇良好的測試輸入資料

  • 優先使用簡單,明確但可以達到測試效果的輸入值
  • 永遠都有測試不完的輸入值
    • 應該要以情境來挑選
    • 最後才是處理例外情況
  • QA
    • Boundary Value
    • decision table

06 測試函數的命名

  • 以情境來命名
    • 適度加上 summary

07 那些測試有何問題 ?

  • 回過頭來看 02 的測試範例

08 測試友善的開發

  • 會想怎麼寫比較好測試
    • 先寫測試在寫程式

09 過度應用本原則

  • 不是為了測試過度改程式碼
  • 測試含蓋率的意義
  • 測試影響了產品開發
    • 重點是功能完成,不是測試完成

10 結語

  • 讓修改和加入新的測試變的更加容易

圖片來源:網路。若分享內容有侵害您的圖片版權,請來信告知,我們會及時加上版權信息,若是您反對使用,本著對版權人尊重的原則,會儘速移除相關內容。