過長方法
過長方法是你會遇到的最常見的程式碼氣味。如果你看到一個超過十行的方法,就應該保持懷疑。每 個方法在第一次提交時都很小,但隨著新功能的到來,開發者經常認為添加「幾行」比適當的重構更容易。
透過每個 sprint 重複這個過程,方法變得越來越長,創造了一連串的問題,使你的程式碼更難維護、測試和理解。
氣味的跡象
方法超過 10 行以上
雖然不是硬性規則,但這是一個值得懷疑的好門檻:
// 警告:這個方法做了太多事情
function processOrder(order) {
// 驗證
if (!order.items || order.items.length === 0) {
throw new Error('訂單必須有項目');
}
if (!order.customer || !order.customer.email) {
throw new Error('訂單必須有客戶電子郵件');
}
// 計算總額
let subtotal = 0;
for (let item of order.items) {
subtotal += item.price * item.quantity;
}
let tax = subtotal * 0.08;
let total = subtotal + tax;
// 套用折扣
if (order.customer.isVIP) {
total *= 0.9;
}
// 處理付款
const paymentResult = paymentService.charge(order.payment, total);
if (!paymentResult.success) {
throw new Error('付款失敗');
}
// 發送確認
emailService.send(order.customer.email, '訂單確認', `總計: $${total}`);
// 更新庫存
for (let item of order.items) {
inventory.reduce(item.productId, item.quantity);
}
return { orderId: generateId(), total, paymentResult };
}
多個抽象層次
當一個方法同時處理高階邏輯和低階細節時:
function generateReport(users) {
// 高階:報告生成邏輯
const report = {
title: '使用者報告',
generatedAt: new Date(),
users: []
};
// 低階:資料處理細節
for (let user of users) {
if (user.isActive && user.lastLoginDate) {
const daysSinceLogin = Math.floor(
(Date.now() - user.lastLoginDate.getTime()) / (1000 * 60 * 60 * 24)
);
if (daysSinceLogin <= 30) {
report.users.push({
name: user.firstName + ' ' + user.lastName,
email: user.email,
lastLogin: user.lastLoginDate.toISOString().split('T')[0],
status: daysSinceLogin <= 7 ? '最近' : '活躍'
});
}
}
}
// 更多低階格式化
report.summary = `${report.users.length} 位活躍使用者(共 ${users.length} 位)`;
return report;
}
為什麼過長方法是有問題的
難以閱讀和理解
當方法變長時,理解和維護變得越來越困難。這很容易導致錯誤和 bug。開發者花更多時間解讀程式碼做什麼,而不是專注於業務邏輯。
違反單一職責原則
過長方法經常違反 SRP,這是 SOLID 設計原則的一部分。一個方法應該只有一個職責,並完全被該職責所封裝。
潛在的副作用
過長方法經常涉及多個子任務,這可能導致意外的行為。這些方法負責比必要更多的任務,而且可能有多個需要變更的原因。
程式碼重複
當比較兩個過長方法時,它們經常包含重複的程式碼。透過將它們拆分成更小的方法,你可以識別它們共享邏輯的方式。
效能問題
「沒有程式碼比沒有程式碼更快。」最快的程式碼通常做最少的工作。過長方法經常做比必要更多的工作,影響效能。
測試困難
如果一個方法執行大量程式碼,它需要大量的測試案例來覆蓋所有可能的路徑。這使測試耗時,並可能導致缺陷。