寫出乾淨函式的黃金法則:新手程式設計師必備指南
更新日期: 2025 年 2 月 3 日
想像你正在組裝樂高城堡,卻把所有零件混在一起亂拼,最後變成無法維護的畸形結構——這就是不良函式設計的後果。
許多新手開發者在寫程式時,容易陷入「能跑就好」的思維,導致程式碼變得難以閱讀、維護和擴充。
《Clean Code》第三章揭示的函式撰寫原則,就像一份「程式設計樂高說明書」,幫助我們打造簡潔、易讀、可維護的程式碼。
本文將透過程式碼示範 + 生活化比喻,帶你掌握寫出乾淨函式的九大黃金法則,讓你的程式碼告別「義大利麵式寫法」,邁向專業工程師之路!
函式結構設計原則
短小精悍法則:函式不是長篇小說
優秀函式的長度標準:
✅ 理想長度:20 行以內
✅ 最佳可讀性:螢幕不滾動即可完整查看
❌ 不良示範:功能混雜的長函式
function processUserData(user) {
// 驗證資料 (15 行)
if (!user.name) throw new Error(...);
if (user.age < 18) throw new Error(...);
// 轉換格式 (10 行)
const formattedUser = { ... };
// 存入資料庫 (20 行)
db.connect();
db.insert(...);
// 發送通知 (15 行)
emailService.send(...);
}
✅ 最佳做法:拆分單一職責函式
function validateUser(user) { /* 驗證邏輯 */ }
function formatUserData(user) { /* 格式轉換 */ }
function saveToDatabase(user) { /* 資料庫操作 */ }
function sendNotification(user) { /* 發送通知 */ }
單一抽象層級:別把食譜寫成化學公式
函式應該維持統一的抽象層級,不能同時包含高層次步驟與細節執行邏輯。
❌ 不良示範:抽象層級混亂
def bake_cake():
prepare_ingredients() # 高層次步驟
oven.set_temperature(180) # 低層次實作
while oven.current_temp < 180:
wait(60)
mix_ingredients() # 回到高層次
put_in_oven()
✅ 最佳做法:封裝底層細節
def preheat_oven(temp):
oven.set_temperature(temp)
while oven.current_temp < temp:
wait(60)
def bake_cake():
prepare_ingredients()
preheat_oven(180) # 抽象化細節
mix_ingredients()
put_in_oven()
二、函式介面設計藝術
命名比你想像的更重要
好的函式命名應該具備清晰的動作與受詞,讓讀者無需進入函式內部,也能理解其功能。
❌ 壞命名 | ✅ 好命名 | 改善點 |
---|---|---|
handleData() | calculateUserAge() | 明確指出計算邏輯 |
doStuff() | validateEmailFormat() | 描述具體驗證行為 |
process() | exportReportToPDF() | 指出輸出格式與目標 |
✅ 最佳命名準則
✔ 動詞 + 受詞組合(例:convertCurrency
)
✔ 長但具描述性,勝過短而模糊
參數數量控制:越少越好
參數數量 vs. 可讀性關係:
- ✅ 0~2 個參數:最佳範圍
- ⚠ 3 個參數:可接受,但要小心
- ❌ 4 個以上參數:應考慮重構
❌ 不良示範:過多參數
public void createUser(String name, int age, String email, String address, boolean isVIP) { ... }
✅ 最佳做法:使用物件封裝參數
public class UserParams {
String name;
int age;
String email;
String address;
boolean isVIP;
}
public void createUser(UserParams params) { ... }
三、函式行為規範
3.1 純函式原則:避免隱藏地雷
❌ 壞味道:副作用函式
let logCount = 0;
function saveOrder(order) {
db.insert(order); // 修改外部狀態
logCount++; // 改變全域變數
sendEmail(); // 觸發其他行為
}
✅ 最佳做法:純函式(Pure Function)
function validateOrder(order) {
return order.items.length > 0 && order.total > 0;
}
純函式不會改變外部變數,更易測試、更可預測。
指令查詢分離:別讓函式精神分裂
❌ 不良示範:修改資料 + 查詢狀態混合
def update_and_check_user(user):
db.update(user)
return db.get_status() # 混合指令與查詢
✅ 最佳做法:分開指令與查詢
def update_user(user):
db.update(user)
def check_user_status(user_id):
return db.get_status(user_id)
錯誤處理與維護性
異常處理 vs. 錯誤碼
情境 | ❌ 錯誤碼 | ✅ 異常處理 |
---|---|---|
檔案不存在 | return -1 | throw new FileNotFoundException() |
網路連線失敗 | return 404 | throw new NetworkException() |
資料驗證失敗 | return false | throw new ValidationError() |
異常處理的好處:
✔ 分離正常邏輯與錯誤處理
✔ 強制呼叫方處理異常狀況
DRY 原則:消滅重複程式碼
❌ 不良示範:重複邏輯
function calculateTotal(price, quantity) {
return price * quantity * 1.08; // 稅率 8%
}
function calculateSubtotal(price, quantity) {
return price * quantity;
}
✅ 最佳做法:提煉重複邏輯
function applyTax(amount, taxRate) {
return amount * (1 + taxRate);
}
function calculateTotal(price, quantity) {
return applyTax(price * quantity, 0.08);
}
結語:乾淨函式帶來的複利效應
遵循這些原則初期可能需要更多思考時間,但會帶來長期收益:
- 除錯時間減少 50%:清晰函式讓問題定位更快
- 新人上手速度提升 3 倍:自解釋的程式碼減少溝通成本
- 功能擴充容易度倍增:模組化設計讓修改影響範圍可控
記住大師 Robert C. Martin 的建議:「寫程式碼的藝術,在於把複雜問題分解成簡單函式的組合」。
從今天起,讓你寫的每個函式都像樂高積木——獨立、完整、可任意組合!