这是基于 Martin Fowler《Refactoring》(第 2 版)的代码异味参考目录。代码异味是更深层问题的表面症状,它们说明代码设计可能出了问题。

“代码异味通常是系统中更深层问题的表面信号。” — Martin Fowler


过度膨胀类异味

表示对象或函数已经大到不再好用。

长函数

迹象:

为什么不好:

可用重构:

示例(重构前):

function processOrder(order) {
  // Validate order (20 lines)
  if (!order.items) throw new Error('No items');
  if (order.items.length === 0) throw new Error('Empty order');
  // ... more validation

  // Calculate totals (30 lines)
  let subtotal = 0;
  for (const item of order.items) {
    subtotal += item.price * item.quantity;
  }
  // ... tax, shipping, discounts

  // Send notifications (20 lines)
  // ... email logic
}

示例(重构后):

function processOrder(order) {
  validateOrder(order);
  const totals = calculateOrderTotals(order);
  sendOrderNotifications(order, totals);
  return { order, totals };
}

大类

迹象:

为什么不好:

可用重构:

检测参考:

代码行数 > 300
方法数量 > 15
字段数量 > 10

基础类型沉迷

迹象:

为什么不好:

可用重构:

示例(重构前):

const user = {
  email: 'john@example.com',     // 只是字符串
  phone: '1234567890',           // 只是字符串
  status: 'active',              // 魔法字符串
  balance: 10050                 // 以分为单位的整数
};

示例(重构后):

const user = {
  email: new Email('john@example.com'),
  phone: new PhoneNumber('1234567890'),
  status: UserStatus.ACTIVE,
  balance: Money.cents(10050)
};

长参数列表

迹象:

为什么不好:

可用重构:

示例(重构前):

function createUser(firstName, lastName, email, phone,
                    street, city, state, zip,
                    isAdmin, isActive, createdBy) {
  // ...
}

示例(重构后):

function createUser(personalInfo, address, options) {
  // personalInfo: { firstName, lastName, email, phone }
  // address: { street, city, state, zip }
  // options: { isAdmin, isActive, createdBy }
}

数据泥团

迹象:

为什么不好:

可用重构:

示例:

// 数据泥团:坐标 (x, y, z)
function movePoint(x, y, z, dx, dy, dz) { }
function scalePoint(x, y, z, factor) { }
function distanceBetween(x1, y1, z1, x2, y2, z2) { }

// 提取 Point3D 类
class Point3D {
  constructor(x, y, z) { }
  move(delta) { }
  scale(factor) { }
  distanceTo(other) { }
}

面向对象滥用类异味

表示 OOP 原则没有被正确使用。

switch 语句

迹象:

为什么不好:

可用重构:

示例(重构前):

function calculatePay(employee) {
  switch (employee.type) {
    case 'hourly':
      return employee.hours * employee.rate;
    case 'salaried':
      return employee.salary / 12;
    case 'commissioned':
      return employee.sales * employee.commission;
  }
}

示例(重构后):

class HourlyEmployee {
  calculatePay() {
    return this.hours * this.rate;
  }
}

class SalariedEmployee {
  calculatePay() {
    return this.salary / 12;
  }
}

临时字段

迹象:

为什么不好:

可用重构:


拒绝遗赠

迹象:

为什么不好:

可用重构:


不同接口的相似类

迹象:

为什么不好:

可用重构:


变更阻碍类异味

这类异味会让改动变得困难,一次改动需要波及很多地方。

发散式变化

迹象:

为什么不好:

可用重构:

示例: 一个 User 类同时因为这些原因变化:

→ 可以拆成:AuthServiceProfileServiceBillingServiceNotificationService


散弹式修改

迹象:

为什么不好:

可用重构:

检测参考: 如果新增一个字段需要改 5 个以上文件,就要警惕。


平行继承体系

迹象:

为什么不好:

可用重构:


可舍弃类异味

表示有些东西已经不必要了,应该移除。

注释过多

迹象:

为什么不好:

可用重构:

好与坏的注释:

// BAD: 解释做了什么
// 遍历用户并检查是否活跃
for (const user of users) {
  if (user.status === 'active') { }
}

// GOOD: 解释为什么
// 只保留活跃用户,未活跃用户由清理任务处理
const activeUsers = users.filter(u => u.isActive);

重复代码

迹象:

为什么不好:

可用重构:

检测规则: 任何重复 3 次以上的代码都应该考虑提取。


惰性类

迹象:

为什么不好:

可用重构:


死代码

迹象:

为什么不好:

可用重构:

检测:

# 查找未使用的导出
# 查找未引用的函数
# IDE 的“unused”警告

臆想泛化

迹象:

为什么不好:

可用重构:


耦合类异味

表示类之间耦合过强。

Feature Envy

迹象:

为什么不好:

可用重构:

示例(重构前):

class Order {
  getDiscountedPrice(customer) {
    // 这里大量使用 customer 数据
    if (customer.loyaltyYears > 5) {
      return this.price * customer.discountRate;
    }
    return this.price;
  }
}

示例(重构后):

class Customer {
  getDiscountedPriceFor(price) {
    if (this.loyaltyYears > 5) {
      return price * this.discountRate;
    }
    return price;
  }
}

不恰当的亲密

迹象:

为什么不好:

可用重构:


消息链

迹象:

为什么不好:

可用重构:

示例:

// 差:消息链
const managerName = employee.getDepartment().getManager().getName();

// 更好:隐藏委派
const managerName = employee.getManagerName();

中间人

迹象:

为什么不好:

可用重构:


严重性指南

严重性 说明 处理方式
Critical 阻塞开发,导致 bug 立刻修复
High 明显增加维护负担 本迭代修复
Medium 有问题但还能接受 近期计划修复
Low 小问题 视情况顺手修

快速检测清单

扫描代码时使用这个清单:


延伸阅读