CleanCode 程式碼氣味 - fantasy0107/notes GitHub Wiki

註解

  1. 不適當的資訊 - 註解的資訊有更適合的地方, 這樣的註解就是不適當的. 可能是原始碼管理系統, 錯誤追蹤, 或任何儲存紀錄的系統
  2. 廢棄的註解 - 註解過時, 不相關或不正確, 就應該更新或註解
  3. 多餘的註解 - 註解應該說明, 程式本身無法解釋的
i++; //increment i 
  1. 寫得不好的註解 - 要寫就要簡潔, 不要廢話
  2. 被註解掉的程式碼 - 看到就刪除它, 反正有程式碼管理系統

開發環境

  1. 需要多個步驟以建立專案或系統 - 應該簡單幾個步驟就可以建立好專案
  2. 需要多個步驟進行測試 - 一個步驟就可以執行所有單元測試

函式

  1. 過多的參數 - 函式的參數不能太多, 沒有最好, 再來是一、二、三,超過三個以上的參數沒有必要
  2. 輸出行參數 - 輸出型參數是不直覺的
appendFooter(s);
  1. 旗標參數 - Boolean 大膽的說出該函式做個超過一件的任務 , 應該被移除
  2. 被遺棄的函式 - 不會在被呼叫的函式應該被移除

一般狀況

  1. 同份原始碼存在多個語言 - 只有一種語言在一個檔案
  2. 明顯該有的行為未被實現 - 遵守最少驚奇原則(the principle of least surprise)
  3. 在邊界上的不正確性 - 不要依賴直你的直覺, 查看所有邊界條件, 然後替這些邊界條件撰寫測試程式
  4. 無視安全規範 - 忽視錯誤會付出代價的
  5. 重複的程式碼 - 最重要的規範之一, 不要重複自己(Don't repeat yourself)
  6. 在錯誤抽象層次上的程式碼 - 建立能劃分高層次的一般概念和低層次的細節概念的抽象模型
  7. 基底類別相依於衍生類別 - 將概念分成基底和衍生類別兩種

類似laravel所有的controller或model都會繼承基底類別Model 或 Controller

  1. 過多的資訊 - 定義良好的模組, 具有少量的介面, 能讓你花費很少的力氣就能完成大量的任務
  2. 被遺棄的程式碼 - 被遺棄的程式碼是不會被執行的
  3. 重直分隔 - 變數和函式應該定義在靠近被使用的地方
  4. 不一致性 - 如果你透過某種方式完成某是, 你就應該透過同樣方式來完成類似的事情
  5. 雜亂的程式碼 - 保持檔案整潔, 不被使用的程式碼應該被移除
  6. 人為的耦合 - 花點時間去想變數, 常數, 函式應該被放在哪裡, 不要隨便丟在某處
  7. 特色留戀
  8. 選擇型參數 - 擁有較多的函式比傳遞某些特定的值到函式裡更好
  9. 迷糊的意圖 - 程式碼要盡可能的表達含意, 跨行程式碼或匈牙利命名法以及魔術數字都會模糊作者的意圖
  10. 錯置的職責 - 根據函式的名稱來決定程式碼應該要被放在哪
  11. 不適當的靜態宣告
  12. 使用具解釋性的變數 - 將程式拆解成許多富有意義的暫存變數
  13. 函式名稱要說到做到 - 如果無法從函式名稱直接看出做甚麼那應該要找一個更好的名稱
  14. 了解演算法
  15. 讓邏輯相依便實體相依
  16. 用多型取代if/else或switch case
  17. 遵循標準的慣例
  18. 要有名稱的常數取代魔術數字
  19. 要精確
  20. 結構勝於常規
  21. 封裝條件判斷
if(shouldBeDeleted(timer)) 比
if(timer.hasExpired() && !timer.isRecurrent()) 好
  1. 避免否定的判斷
if(buffer.shouldCompact()) 比
if(!buffer.shouldNotCompact()) 好
  1. 函式應該只做一件事
//做了三件事
public void pay() {
  for(Employee e : employees) {
    if (e.isPayday()) {
      Money pay = e.calculatePay();
      e.deliverPay(pay);
    }
  }
}

//拆解成下面
public void pay()
{
   for(Employee e : employees)
     payIfNecessary(e);
}

public void payIfNecessary(Employee e) {
    if (e.isPayday()) {
       calculateAndDeliverPay()e;
    }
}

public void calculateAndDeliverPay(Employee e) {
   Money pay = e.calculatePay();
   e.deliverPay(pay);
}
  1. 隱藏時序耦合 - 排列參數與函式順序
  2. 不要隨意 - 組織程式碼要有理由
  3. 封裝邊界條件
if (level +1 < tags.length) {
    parts = new Parse(body, tags, level +1, offset + endTag);
    body = null;
}

//封裝
int nextLevel = level + 1 ;
if (nextLevel  < tags.length) {
    parts = new Parse(body, tags, nextLevel , offset + endTag);
    body = null;
}
  1. 函式內容應該下降抽象層次一層
  2. 可調整的資料應該放置於高層次
  3. 避免傳導性導覽

命名

  1. 選擇具描述性質的名稱 - 命名不用急著確定, 必須要確保這個命名具有足夠的描述性
//Bad
$m = 10;

//good
#$month = 10;
  1. 在適當的抽象層次選擇適當命名 - 不要選擇透露實現內容訊息的名稱, 選擇能反映出類別或函式抽象層次的名稱
  2. 盡可能用標準命名法 - 如果使用已有的慣例或用法來進行命名, 那些命名會比較容易被理解
  3. 非模稜兩可的名稱 - 選擇不會讓函式或變數意義模稜兩可的名稱
  4. 較大範圍的視野使用較長的名稱 - 名稱的長度應該和視野的範圍大小有關
  5. 避免編碼 - 不應該將型態或視野編碼到名稱裡
class Item
{
    private $i_nmae;
    private $i_type;
}
  1. 命名應該描述可能的程式副作用 - 名稱應該描述函式, 變數或類別所有事情

測試

  1. 不足夠的測試
  2. 使用覆蓋率工具
  3. 不要跳過簡單的測試
  4. 被忽略的測試是對模稜兩可的疑問
  5. 測試邊界條件
  6. 在程式錯誤附近進行詳盡的測試
  7. 失敗的模式是某種啟示
  8. 測試涵蓋率模式可以是一種啟示
  9. 測試要夠快速