初探 Clean Code


  1. 說明
    1. Why Clean Code
  2. 原則與技巧
    1. Naming
    2. Conditoinals
    3. Clean Method
    4. Clean Class
    5. Clean Comment
  3. 實作
    1. 檢驗指標
    2. Code Smells
  4. 參考資料

筆記關於 Clean Code 的設計實務之道。

logo

說明

Clean Code 強調的是程式碼的可讀性 (Readability),因為任何人都可以寫出電腦看得懂 (Compiler & Interpreter 可以通過) 的程式碼,但卻很難寫出讓未來的自己或其他人看得懂的程式碼。

筆記源自 Cory House 在 Pluralsight 的 Clean Coding Principles in C#

進階參考資源包含 Steve McConnell 的 Code Complete、Robert C.Martin 的 Clean Code 以及 Andrew Hunt 與 David Thomas 的 The Pragmatic Programmer


根據 Cory House 指出,Clean Code 有三個原則,分別是用正確的工具處理工作、讓程式碼呈現資訊而非噪音 (signal to noise ratio) 以及讓程式碼本身就是文件 (Self-Documenting)。

用正確的工具處理工作

注意程式碼的邊界,不要互相干涉以及混雜再一起,例如 MVC 專案當中 C#、HTML、CSS 以及 JS 各司其職,不要越俎代庖 (避免用一種程式語言去組合另一種程式語言),儘量用 Convention over Configuration 以及大家有共識的方式去處理問題。

太過複雜的 Regex 不要用,相較於硬體資源上的效率,可讀性仍是處在第一位重要。

讓程式碼呈現資訊而非噪音

訊號 (signal) 的特質包含 TED (Terse, Expressive, Does one thing);相對地噪音 (noise) 則是具有高 cyclomatic complexity、浮誇的縮排、不好的命名、重複的程式碼、冗長的單一函式以及巨大的類別等。

讓程式碼本身就是文件

透過程式碼排版的技巧來增進可讀性,讓程式碼的縮排 (Indent)與層次 (Layer)等結構本身向閱讀者傳達訊息,而非透過註解來傳達。

Why Clean Code

在程式碼的開發過程中,反覆閱讀程式碼的時間勝過撰寫程式碼,因此程式碼的可讀性特別重要,而好閱讀的程式碼能夠為需求改變或功能擴充帶來更快的實踐。

原則與技巧

Naming

類別的命名,使用明確的名詞,命名要能夠反映出單一責任與高內聚的特性,例如 User, Account, QueryBuilder, ProductRepository。

避免命名出現 And, If, Or,如果有這樣的命名表示類別的責任太多了,應該再切割為更小的類別

命名必須要與信任感,不要有命名預期外的狀況 (例如讀取的功能卻會造成資料寫入)

布林值的命名技巧: isOpem, isActive, loggedIn

Conditoinals

適當的簡化條件的布林值,可以提升程式碼可讀性:

if (loggedIn == ture)

✔️
if (loggedIn)
bool doSomeThing;
if (weather == Weather.Sunny)
  doSomeThing = true;
else
  doSomeThing = false;

✔️
bool doSomeThing = weather == Weather.Sunny;

在條件撰寫上避免使用雙重否定。

適當使用三元運算子 (Ternary) 增進可讀性。

使用強型別 (Enum) 進行比較,減少使用字串比較。

避免使用 Magic Number (使用有意義命名的變數或者 Enum 替代)。

如果條件的判斷式有多項,另外抽成有意義命名的變數替代,增進可讀性。

if (employee.Age > 65 && employee.IsRetired)

✔️
bool eligibleForPension = employee.Age > 65 && employee.IsRetired;
if (eligibleForPension)

如果複雜到變數 hold 不住,就抽出封裝成 Method。

使用多形 (Polymorphism) 取代 Switch 分歧。

使用陳述性的方式處理邏輯 (Linq 的 Predicate)。

提高程式碼的可維護性,使用 Table Driven 將資料儲存在資料庫當中,而非直接在程式碼的邏輯中處理。

Clean Method

考慮將程式碼抽為 Method 或 Function 的時機,包含發現程式碼出現相似的規律 (Pattern) 以及重複的程式碼時;出現箭頭形狀的縮排 (Arrow Code) 造成程式碼可讀性下降時;程式碼的用途不明確時。

此外在處理 Arrow Code 時,除了抽為 Method,也可以利用 Fail Fast 的技巧,打破巢狀的 If 讓會報錯直接被處理。

public void RegisterUser(string username, string password)
{
  if (!string.isNullOrWhitespace(username))
  {
    if (!string.isNullOrWhitespace(password))
    {
      ...
    }
    else
    {
      throw new ArgumentException();
    }
    throw new ArgumentException();
  }
}

✔️
public void RegisterUser(string username, string password)
{
  if (string.isNullOrWhitespace(username))
  {
    throw new ArgumentException();
  }
  if (string.isNullOrWhitespace(password))
  {
    throw new ArgumentException();
  }
}

而當程式碼的用途不明確時,也應該抽為 Method,例如:

if ((fileExtension == ".jpg" || fileExtension == ".png" ) && isFileExists)

✔️
private bool ValidaFileExists(string fileExtension, bool isFileExists)
{
  var validFileExtensions = new List<string>() {".jpg", ".png"};

  return validFileExtensions.Contains(fileExtension) && isFileExists;

}

💡 在 Method 內部的變數使用上,不要集中宣告在開頭,而是要緊鄰使用此內部變數的程式碼段落,讓閱讀上可以更容易識別變數的用途。

💡 如果 Method 的參數過多,可以將參數另外設計為類別 (Class) 提升可讀性。

💡 避免在 Method 加入 Flag 參數,這樣會讓單一 Method 負擔額外的工作,分開成不同的 Method。

Clean Class

類別的設計上,首重高內聚 (Cohesion),設計上要避免讓類別淪為龐雜的程式碼聚合,以提升類別程式碼的可讀性以及被重複使用的可能。(相對於類別過於龐雜的問題,很少發生類別被抱怨設計的太”迷你”)。

在物件導向設計上,要避免 Primitive Obsession (偏好使用 int, double, string 等基本型別) 而忽略使用類別 (Class) 藉由封裝所帶來的可讀性提升。

Clean Comment

正:寫註解是為了提升程式碼的可讀性

反:不寫註解,讓程式碼表現意義則是提升程式碼的可讀性

合:原則上不寫註解,正確的使用註解以提升可讀性

💡 避免廢話類型的註解 (這個變數宣告為 int 並賦值)

💡 避免用註解表現程式碼用途,使變數、方法、類別的命名賦予用途

💡 移除註解掉的程式碼,讓版本控制工具來管理被移除的程式碼

💡 移除分隔程式碼段落的註解以及標註程式碼起始的註解

💡 移除作者資訊以及程式碼用途摘要

使用 Visual Studio 所支援的 //TODO 以及 //HACK,對於類別或者方法,使用 Summary

實作

檢驗指標

Maintainability Index

Cyclomatic

Code Smells

Arrow Code

  • Nested Ifs
  • Guard Clause
  • Indents

Comments

  • Repeat
  • Version Control

Mayfly Variables

  • Check dependency

Failing Fast

Table Driven

Tests before Refactoring

Avoid Abbreviation

  • Long descriptive name is accepted

TED Principles

  • Terse
  • Expressive
  • Doe one thing

Readability

  • Naming
  • Short & Simple
  • Linq Predicate

Extension Method over Loop Methods

Least Surprise Principle

參考資料

Clean Code concepts adapted for .NET/.NET Core