CleanCode 程式碼氣味 - fantasy0107/notes GitHub Wiki
註解
- 不適當的資訊 - 註解的資訊有更適合的地方, 這樣的註解就是不適當的. 可能是原始碼管理系統, 錯誤追蹤, 或任何儲存紀錄的系統
- 廢棄的註解 - 註解過時, 不相關或不正確, 就應該更新或註解
- 多餘的註解 - 註解應該說明, 程式本身無法解釋的
i++; //increment i
- 寫得不好的註解 - 要寫就要簡潔, 不要廢話
- 被註解掉的程式碼 - 看到就刪除它, 反正有程式碼管理系統
開發環境
- 需要多個步驟以建立專案或系統 - 應該簡單幾個步驟就可以建立好專案
- 需要多個步驟進行測試 - 一個步驟就可以執行所有單元測試
函式
- 過多的參數 - 函式的參數不能太多, 沒有最好, 再來是一、二、三,超過三個以上的參數沒有必要
- 輸出行參數 - 輸出型參數是不直覺的
appendFooter(s);
- 旗標參數 - Boolean 大膽的說出該函式做個超過一件的任務 , 應該被移除
- 被遺棄的函式 - 不會在被呼叫的函式應該被移除
一般狀況
- 同份原始碼存在多個語言 - 只有一種語言在一個檔案
- 明顯該有的行為未被實現 - 遵守最少驚奇原則(the principle of least surprise)
- 在邊界上的不正確性 - 不要依賴直你的直覺, 查看所有邊界條件, 然後替這些邊界條件撰寫測試程式
- 無視安全規範 - 忽視錯誤會付出代價的
- 重複的程式碼 - 最重要的規範之一, 不要重複自己(Don't repeat yourself)
- 在錯誤抽象層次上的程式碼 - 建立能劃分高層次的一般概念和低層次的細節概念的抽象模型
- 基底類別相依於衍生類別 - 將概念分成基底和衍生類別兩種
類似laravel所有的controller或model都會繼承基底類別Model 或 Controller
- 過多的資訊 - 定義良好的模組, 具有少量的介面, 能讓你花費很少的力氣就能完成大量的任務
- 被遺棄的程式碼 - 被遺棄的程式碼是不會被執行的
- 重直分隔 - 變數和函式應該定義在靠近被使用的地方
- 不一致性 - 如果你透過某種方式完成某是, 你就應該透過同樣方式來完成類似的事情
- 雜亂的程式碼 - 保持檔案整潔, 不被使用的程式碼應該被移除
- 人為的耦合 - 花點時間去想變數, 常數, 函式應該被放在哪裡, 不要隨便丟在某處
- 特色留戀
- 選擇型參數 - 擁有較多的函式比傳遞某些特定的值到函式裡更好
- 迷糊的意圖 - 程式碼要盡可能的表達含意, 跨行程式碼或匈牙利命名法以及魔術數字都會模糊作者的意圖
- 錯置的職責 - 根據函式的名稱來決定程式碼應該要被放在哪
- 不適當的靜態宣告
- 使用具解釋性的變數 - 將程式拆解成許多富有意義的暫存變數
- 函式名稱要說到做到 - 如果無法從函式名稱直接看出做甚麼那應該要找一個更好的名稱
- 了解演算法
- 讓邏輯相依便實體相依
- 用多型取代if/else或switch case
- 遵循標準的慣例
- 要有名稱的常數取代魔術數字
- 要精確
- 結構勝於常規
- 封裝條件判斷
if(shouldBeDeleted(timer)) 比
if(timer.hasExpired() && !timer.isRecurrent()) 好
- 避免否定的判斷
if(buffer.shouldCompact()) 比
if(!buffer.shouldNotCompact()) 好
- 函式應該只做一件事
//做了三件事
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);
}
- 隱藏時序耦合 - 排列參數與函式順序
- 不要隨意 - 組織程式碼要有理由
- 封裝邊界條件
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;
}
- 函式內容應該下降抽象層次一層
- 可調整的資料應該放置於高層次
- 避免傳導性導覽
命名
- 選擇具描述性質的名稱 - 命名不用急著確定, 必須要確保這個命名具有足夠的描述性
//Bad
$m = 10;
//good
#$month = 10;
- 在適當的抽象層次選擇適當命名 - 不要選擇透露實現內容訊息的名稱, 選擇能反映出類別或函式抽象層次的名稱
- 盡可能用標準命名法 - 如果使用已有的慣例或用法來進行命名, 那些命名會比較容易被理解
- 非模稜兩可的名稱 - 選擇不會讓函式或變數意義模稜兩可的名稱
- 較大範圍的視野使用較長的名稱 - 名稱的長度應該和視野的範圍大小有關
- 避免編碼 - 不應該將型態或視野編碼到名稱裡
class Item
{
private $i_nmae;
private $i_type;
}
- 命名應該描述可能的程式副作用 - 名稱應該描述函式, 變數或類別所有事情
測試
- 不足夠的測試
- 使用覆蓋率工具
- 不要跳過簡單的測試
- 被忽略的測試是對模稜兩可的疑問
- 測試邊界條件
- 在程式錯誤附近進行詳盡的測試
- 失敗的模式是某種啟示
- 測試涵蓋率模式可以是一種啟示
- 測試要夠快速