Files
claude-howto/zh/03-skills/refactor/references/code-smells.md
T
Luong NGUYEN 89e89d4aa3 feat(zh): add Chinese translations in zh/ directory
Add Chinese (Simplified) translations for all documentation, organized
under a dedicated zh/ directory that mirrors the English folder structure.

Co-authored-by: tanqingkuang <tanqingkuang@users.noreply.github.com>

Translations originally contributed by @tanqingkuang in #45.
Restructured from *-CN.md suffix pattern into zh/ directory to prevent
the EPUB builder (scripts/build_epub.py collect_folder_files) from
picking up Chinese files via glob("*.md") inside module folders.
2026-04-06 23:08:54 +02:00

670 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 代码异味目录
这是基于 Martin Fowler《Refactoring》(第 2 版)的代码异味参考目录。代码异味是更深层问题的表面症状,它们说明代码设计可能出了问题。
> “代码异味通常是系统中更深层问题的表面信号。” — Martin Fowler
---
## 过度膨胀类异味
表示对象或函数已经大到不再好用。
### 长函数
**迹象:**
- 函数超过 30-50 行
- 需要滚动才能看完整个函数
- 嵌套层级很多
- 需要注释来解释局部逻辑
**为什么不好:**
- 难理解
- 难单独测试
- 改动容易波及其他逻辑
- 重复逻辑容易藏在里面
**可用重构:**
- Extract Method
- Replace Temp with Query
- Introduce Parameter Object
- Replace Method with Method Object
- Decompose Conditional
**示例(重构前):**
```javascript
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
}
```
**示例(重构后):**
```javascript
function processOrder(order) {
validateOrder(order);
const totals = calculateOrderTotals(order);
sendOrderNotifications(order, totals);
return { order, totals };
}
```
---
### 大类
**迹象:**
- 实例变量很多(>7-10
- 方法很多(>15-20
- 类名含糊(Manager、Handler、Processor
- 方法没有用到全部字段
**为什么不好:**
- 违反单一职责原则
- 难测试
- 改动会波及无关功能
- 难以复用局部能力
**可用重构:**
- Extract Class
- Extract Subclass
- Extract Interface
**检测参考:**
```text
代码行数 > 300
方法数量 > 15
字段数量 > 10
```
---
### 基础类型沉迷
**迹象:**
- 用基础类型表达领域概念(用 string 表示邮箱、用 int 表示金额)
- 使用基础类型数组,而不是对象
- 用字符串常量表示类型码
- 大量 magic number / magic string
**为什么不好:**
- 类型层面没有验证
- 逻辑散落在整个代码库里
- 容易传错值
- 缺少领域对象
**可用重构:**
- Replace Primitive with Object
- Replace Type Code with Class
- Replace Type Code with Subclasses
- Replace Type Code with State/Strategy
**示例(重构前):**
```javascript
const user = {
email: 'john@example.com', // 只是字符串
phone: '1234567890', // 只是字符串
status: 'active', // 魔法字符串
balance: 10050 // 以分为单位的整数
};
```
**示例(重构后):**
```javascript
const user = {
email: new Email('john@example.com'),
phone: new PhoneNumber('1234567890'),
status: UserStatus.ACTIVE,
balance: Money.cents(10050)
};
```
---
### 长参数列表
**迹象:**
- 方法参数超过 4 个
- 一些参数总是一起出现
- 布尔参数改变方法行为
- 经常传 null / undefined
**为什么不好:**
- 难正确调用
- 参数顺序容易搞混
- 暗示方法做了太多事
- 难以增加新参数
**可用重构:**
- Introduce Parameter Object
- Preserve Whole Object
- Replace Parameter with Method Call
- Remove Flag Argument
**示例(重构前):**
```javascript
function createUser(firstName, lastName, email, phone,
street, city, state, zip,
isAdmin, isActive, createdBy) {
// ...
}
```
**示例(重构后):**
```javascript
function createUser(personalInfo, address, options) {
// personalInfo: { firstName, lastName, email, phone }
// address: { street, city, state, zip }
// options: { isAdmin, isActive, createdBy }
}
```
---
### 数据泥团
**迹象:**
- 同样的 3 个以上字段反复一起出现
- 参数总是成对或成组传递
- 类中的字段子集总是一起使用
**为什么不好:**
- 逻辑重复
- 缺少抽象
- 难扩展
- 暗示隐藏的类应该存在
**可用重构:**
- Extract Class
- Introduce Parameter Object
- Preserve Whole Object
**示例:**
```javascript
// 数据泥团:坐标 (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 语句
**迹象:**
- 长 switch/case 或 if/else 链
- 多处出现相同 switch
- 按类型码分支
- 新增 case 时需要改很多地方
**为什么不好:**
- 违反开放 / 封闭原则
- 改动会扩散到所有 switch 位置
- 难扩展
- 往往说明缺少多态
**可用重构:**
- Replace Conditional with Polymorphism
- Replace Type Code with Subclasses
- Replace Type Code with State/Strategy
**示例(重构前):**
```javascript
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;
}
}
```
**示例(重构后):**
```javascript
class HourlyEmployee {
calculatePay() {
return this.hours * this.rate;
}
}
class SalariedEmployee {
calculatePay() {
return this.salary / 12;
}
}
```
---
### 临时字段
**迹象:**
- 实例变量只在部分方法中使用
- 字段只在某些条件下设置
- 某些情况初始化过程很复杂
**为什么不好:**
- 容易混淆:字段存在但可能为 null
- 对象状态难理解
- 说明条件逻辑被隐藏了
**可用重构:**
- Extract Class
- Introduce Null Object
- Replace Temp Field with Local
---
### 拒绝遗赠
**迹象:**
- 子类没有用到继承来的方法 / 数据
- 子类重写方法只是为了不做事
- 继承被当成代码复用,而不是真正的 IS-A
**为什么不好:**
- 抽象不对
- 违反里氏替换原则
- 继承层次误导人
**可用重构:**
- Push Down Method / Field
- Replace Subclass with Delegate
- Replace Inheritance with Delegation
---
### 不同接口的相似类
**迹象:**
- 两个类做的事情差不多
- 同一个概念却用了不同方法名
- 理论上可以互换使用
**为什么不好:**
- 重复实现
- 没有公共接口
- 难切换
**可用重构:**
- Rename Method
- Move Method
- Extract Superclass
- Extract Interface
---
## 变更阻碍类异味
这类异味会让改动变得困难,一次改动需要波及很多地方。
### 发散式变化
**迹象:**
- 一个类因为很多不同原因而被修改
- 不同区域的变更都会触发同一个类的编辑
- 类变成了“上帝类”
**为什么不好:**
- 违反单一职责原则
- 变更频率高
- 容易产生合并冲突
**可用重构:**
- Extract Class
- Extract Superclass
- Extract Subclass
**示例:**
一个 `User` 类同时因为这些原因变化:
- 身份验证变化
- 个人资料变化
- 计费变化
- 通知变化
→ 可以拆成:`AuthService``ProfileService``BillingService``NotificationService`
---
### 散弹式修改
**迹象:**
- 一个改动需要编辑很多类
- 一个小功能要触碰 10+ 个文件
- 改动分散,难以一次找全
**为什么不好:**
- 很容易漏改
- 耦合高
- 容易出错
**可用重构:**
- Move Method
- Move Field
- Inline Class
**检测参考:**
如果新增一个字段需要改 5 个以上文件,就要警惕。
---
### 平行继承体系
**迹象:**
- 在一个继承体系中创建子类,另一个体系也得跟着创建
- 类前缀相似(如 `DatabaseOrder``DatabaseProduct`
**为什么不好:**
- 维护成本翻倍
- 两个层次之间耦合
- 容易漏掉一边
**可用重构:**
- Move Method
- Move Field
- 消除其中一个层次
---
## 可舍弃类异味
表示有些东西已经不必要了,应该移除。
### 注释过多
**迹象:**
- 注释在解释代码“做了什么”
- 注释掉的代码
- 长期存在的 TODO / FIXME
- 注释里带道歉语气
**为什么不好:**
- 注释会过时
- 代码本身应该能自解释
- 死代码会造成混乱
**可用重构:**
- Extract Method(让方法名解释意图)
- Rename(通过命名提升清晰度)
- 删除注释掉的代码
- Introduce Assertion
**好与坏的注释:**
```javascript
// BAD: 解释做了什么
// 遍历用户并检查是否活跃
for (const user of users) {
if (user.status === 'active') { }
}
// GOOD: 解释为什么
// 只保留活跃用户,未活跃用户由清理任务处理
const activeUsers = users.filter(u => u.isActive);
```
---
### 重复代码
**迹象:**
- 多处出现相同代码
- 只有少量差异的相似代码
- 复制粘贴模式
**为什么不好:**
- 修复要改多处
- 容易不一致
- 代码库臃肿
**可用重构:**
- Extract Method
- Extract Class
- Pull Up Method(在继承层次中)
- Form Template Method
**检测规则:**
任何重复 3 次以上的代码都应该考虑提取。
---
### 惰性类
**迹象:**
- 类做的事情不足以支撑它的存在
- 只是一个没有增值的包装器
- 过度设计的产物
**为什么不好:**
- 增加维护开销
- 多了一层没必要的间接
- 有复杂度但没收益
**可用重构:**
- Inline Class
- Collapse Hierarchy
---
### 死代码
**迹象:**
- 无法到达的代码
- 未使用的变量 / 方法 / 类
- 注释掉的代码
- 位于不可能条件分支后的代码
**为什么不好:**
- 造成困惑
- 增加维护负担
- 降低理解速度
**可用重构:**
- Remove Dead Code
- Safe Delete
**检测:**
```bash
# 查找未使用的导出
# 查找未引用的函数
# IDE 的“unused”警告
```
---
### 臆想泛化
**迹象:**
- 只有一个子类的抽象类
- 为“未来可能需要”而加的未使用参数
- 只是转发的函数
- 为一个用例设计的“框架”
**为什么不好:**
- 复杂度没有收益
- 违背 YAGNI
- 更难理解
**可用重构:**
- Collapse Hierarchy
- Inline Class
- Remove Parameter
- Rename Method
---
## 耦合类异味
表示类之间耦合过强。
### Feature Envy
**迹象:**
- 一个方法使用别的类的数据比自己更多
- 对另一个对象调用很多 getter
- 数据和行为分离
**为什么不好:**
- 行为放错地方了
- 封装性差
- 难维护
**可用重构:**
- Move Method
- Move Field
- Extract Method(然后移动)
**示例(重构前):**
```javascript
class Order {
getDiscountedPrice(customer) {
// 这里大量使用 customer 数据
if (customer.loyaltyYears > 5) {
return this.price * customer.discountRate;
}
return this.price;
}
}
```
**示例(重构后):**
```javascript
class Customer {
getDiscountedPriceFor(price) {
if (this.loyaltyYears > 5) {
return price * this.discountRate;
}
return price;
}
}
```
---
### 不恰当的亲密
**迹象:**
- 类之间互相访问私有细节
- 双向引用很多
- 子类知道父类太多细节
**为什么不好:**
- 耦合高
- 改一边会连带另一边
- 难单独修改
**可用重构:**
- Move Method
- Move Field
- Change Bidirectional to Unidirectional
- Extract Class
- Hide Delegate
---
### 消息链
**迹象:**
- 方法调用链很长:`a.getB().getC().getD().getValue()`
- 客户端依赖导航结构
- “火车残骸”式代码
**为什么不好:**
- 很脆弱,任何一层变动都会坏
- 违反迪米特法则
- 绑定到对象结构
**可用重构:**
- Hide Delegate
- Extract Method
- Move Method
**示例:**
```javascript
// 差:消息链
const managerName = employee.getDepartment().getManager().getName();
// 更好:隐藏委派
const managerName = employee.getManagerName();
```
---
### 中间人
**迹象:**
- 类只是把调用转发给另一个类
- 一半以上的方法都是委派
- 没有额外价值
**为什么不好:**
- 多了一层没必要的间接
- 维护成本更高
- 架构令人困惑
**可用重构:**
- Remove Middle Man
- Inline Method
---
## 严重性指南
| 严重性 | 说明 | 处理方式 |
|--------|------|---------|
| **Critical** | 阻塞开发,导致 bug | 立刻修复 |
| **High** | 明显增加维护负担 | 本迭代修复 |
| **Medium** | 有问题但还能接受 | 近期计划修复 |
| **Low** | 小问题 | 视情况顺手修 |
---
## 快速检测清单
扫描代码时使用这个清单:
- [ ] 有没有超过 30 行的函数?
- [ ] 有没有超过 300 行的类?
- [ ] 有没有参数超过 4 个的函数?
- [ ] 有没有重复代码块?
- [ ] 有没有按类型码分支的 switch/case
- [ ] 有没有未使用的代码?
- [ ] 有没有大量使用其他类数据的方法?
- [ ] 有没有很长的方法调用链?
- [ ] 有没有解释“是什么”而不是“为什么”的注释?
- [ ] 有没有应该封装成对象的基础类型?
---
## 延伸阅读
- Fowler, M. (2018). *Refactoring: Improving the Design of Existing Code* (2nd ed.)
- Kerievsky, J. (2004). *Refactoring to Patterns*
- Feathers, M. (2004). *Working Effectively with Legacy Code*