# 程式碼的氣味與啟發 --- ## 報告內容 - Clean Code 無瑕的程式碼 - 程式碼的氣味與啟發 - 程式碼的原則 及 需注意的部分 - 協助編輯 改善 程式,引導產出更多的程式碼 --- ## 原則 - 最小驚奇原則 ```系統的行為應表現的與大部分人預期的相同``` - 如同預期 - 避免猜測、嘗試 - 節省時間及資源 ---- - 單一職責原則 - 一個單元、一個任務 - 簡潔有力 - 直覺的理解及運行 - 內聚性 ---- - 閱讀性 - 優雅、效率、紀律、整潔 - 愉快的閱讀 - 避免破窗效應理論 - 唯一有效的「程式品質」度量單位:每分鐘罵髒話的次數 - 命名名稱:**有力的表達** ---- - 童子軍規則 ```每次經手程式,都應該使它能比經手前好一些``` - 經手並改善 - 對小事負責 --- ## 關鍵詞 ---- - 不適當的、廢棄的、被遺棄的、多餘的、被註解掉的 - 不論是註解、函式、變數,都不應該有符合以上關鍵詞 - 善用版控,讓那些應該存在版控裡的資訊,留在板控裡面 - 刪掉它們:有需要時,再從版控提取即可 ---- - 反直覺的 - 使一個單元,負責了不應屬於它的任務 - 輸出型參數:參數應用於輸入,而非輸出 - 旗標參數:函數應只負責一個任務,有旗標參數表示負責了超過一個的任務 ---- - 反直覺的 - 使一個預期的行為未被實現 - 與上述 最小驚奇原則 連結 - **任何函式或類別** 應該實現其他程式設計師 **合理預期該有的行為** - 反之,則會破壞程式設計師間的信任 => 需回頭閱讀其他程式 ---- - 複雜的 - 建立專案或系統 - 不應需用瑣碎的步驟才能建立 - 不應需用東找西找來尋找程式所需的資源 - 應要能利用一個簡單的指令以完成 ---- - 複雜的 - 測試 - 最好的情況:只需按下IDE的某個按鈕 - 最糟的情況:只需於指令環境輸入一個簡單指令 - 其他 - 原始檔使用的語言 --- ## 重要且常見的狀況 ---- ### 標準化 - 同份原始檔存在多種語言 - 遵循標準的慣例 ---- ### 直覺化 - 最小驚奇法則 - 明顯該有的行為未被實現 - 不一致性 - 垂直分隔 - 減少宣告與使用間的垂直距離 - 人為的耦合 - 兩個模組間「沒有直接目的」的耦合 ---- ### 直覺化 - 特色留戀 - 類別不應對其他類別的變數或函數感興趣 - 模糊的意圖 - 需要花額外時間以了解 - 函式名稱要說到做到 ---- ### 正確性 - 在邊界上的不正確行為 - 不要依賴直覺 - 查看所有邊界條件 => 為邊界條件撰寫程式 - 無視安全規範 - 無視警告、忽略失敗的測試 可能可以節省時間 - 卻能造成更大的損害 - 錯置的職責 - 正確的函式在正確的類別下 ---- ### 正確性 - 不適當的靜態宣告 - 可使用多型替代 - 要精確 - 不要隨意 - 結構勝於常規 ---- ### 精簡化 - 重複的程式碼 - 一次,就只能一次 - 過多的資訊 - 減少類別所擁有的(方法/變數/實體變數) - 被遺棄的程式碼 - 不要讓不被需要的程式被編譯 ---- ### 精簡化 - 選擇型參數 - 單一職責原則 - 了解演算法 - 函式應該只做一件事 - 避免傳遞性導覽 ```python= a.getB().getC().doSomething() ``` ---- ### 閱讀性 - 雜亂的程式 - 不會被使用的變數 - 不會被呼叫的函式 - 沒有資訊的註解 - 使用具解釋性的變數 - 可讀性 ---- ### 閱讀性 - 用有名稱的常數取代魔術數字 - 利用 Boolean 變數來封裝其邏輯 - 封裝條件判斷 - 封裝邊界條件 - 避免否定的條件判斷 - 隱藏時序耦合 - 排序函式參數,以凸顯其呼叫次序 ---- ### 類別設計 ``` 「高層次的一般概念」 & 「低層次的細節概念」 建立抽象類別 -> 包含高層次的概念 建立衍生類別 -> 包含低層次的概念 ``` - 在錯誤抽象層次上的程式碼 - 確保「**所有的** 低層次概念都放在衍生類別內」 - 確保「**所有的** 高層次概念都放在基底類別內」 ---- ### 類別設計 - 基底類別相依於其衍生類別 - 高層次基底類別,可以 **獨立*** 於低層次衍生類別 - 讓邏輯相依變成實體相依 - 使類別可以自己處理自己的事務 - 德摩特爾法則 - 模組不該知道「關於它所操縱物件的內部運作」 ---- #### 類別設計 - 用多型取代 if/else 或 switch/case - 對於給定的選擇型態,不應該有超過一個以上的 switch 敘述 ```java= program Adhoc; function Add(x, y : Integer) : Integer; begin Add := x + y end; function Add(s, t : String) : String; begin Add := Concat(s, t) end; begin Writeln(Add(1, 2)); (* 打印"3" *) Writeln(Add('Hello, ', 'Mammals!')); (* 打印"Hello, Mammals!" *) end. ``` ---- #### 類別設計 - 函式內容應該下降抽象層次一層 - 將不同層次的抽象概念分隔開 ```javascript= public String render() throws Exception { StringBuffer html = new StringBuffer("<hr"); if(size > 0) html.append(" size=\"").append(size + 1).append("\""); html.append(">"); return html.toString(); } ``` ---- #### 類別設計 - 函式內容應該下降抽象層次一層 - 將不同層次的抽象概念分隔開 ```javascript= public String render() throws Exception { HtmlTag hr = new HtmlTag("hr"); if (extraDashed > 0) hr.addAttribute("size", hrSize(extraDashed)) } private String hrSize(int height) { int hrSize = height + 1; return String.format("%d", hrSize) } ``` ---- #### 類別設計 - 可調整的資料應放置於高階層次 - 常數:如預設值/設定值 - 已知預期應該存在於高階抽象層次上->放在高階處 - 不要把它留在低階處 ---- ### 命名 - 選擇具描述性質的名稱 - 避免誤導 - 產生有意義的區別 - 在適當的抽象層次選擇適當的命名 - 盡可能使用標準命名法 - 駝峰式大小寫 ---- ### 命名 - 非模稜兩可的名稱 - 避免雙關語 - 較大範圍的視野使用較長的名字 - 避免編碼 - 匈牙利命名法 - 命名應該描述可能的程式副作用 ---- ### 測試 - F.I.R.S.T - Fast - 快速的測試,促進更多的測試 - Independent - 依賴掩蓋後續的失敗 - Repeatable - 任何環境都可執行 - Self-Validating - 自我驗證 - Timely - 程式服務測試 ---- ### 測試 - 不足夠的測試 - 使用涵蓋率工具 - 不要跳過簡單的測試 - 被忽略的測試是對模稜兩可的疑問 - 測試邊界條件 - 在程式錯誤附近進行詳盡的測試 - 失敗的模式是某種啟示 - 測試涵蓋率模式可以是一種啟示 - 測試要夠快速 --- ## 心得 ---- #### Ærlighed i små ting er ikke nogen lille ting. #### 在小事情上誠實,可不是一件小事
{"metaMigratedAt":"2023-06-15T22:16:46.324Z","metaMigratedFrom":"Content","title":"程式碼的氣味與啟發","breaks":true,"contributors":"[{\"id\":\"16b1c756-1043-40ce-ac28-4579eb28305d\",\"add\":4384,\"del\":394}]"}
    168 views