name: refactoring description: 通过小步迭代进行系统化重构。当用户提到“重构”、“清理”、“重组”、“提取”、“重命名”、“简化”或识别到代码异味时使用。强制执行“一次变更 → 测试 → 提交”的循环。用于结构改进,而非样式/格式化(请使用 /lint)。不用于添加新功能或修复错误。 allowed-tools: ‘*’
重构
在不改变行为的前提下改进代码结构。一次一小步。
铁律: 一次重构 → 测试 → 提交。绝不批量更改。
何时使用
按顺序回答。在第一个匹配项处停止:
- 用户说“重构”、“清理”、“重组”? → 使用此技能
- 用户要求“提取”、“重命名”、“简化”? → 使用此技能
- 识别到代码异味? → 使用此技能
- 用户想要添加功能或修复错误? → 跳过(使用 tdd-enforcer)
- 用户想要格式化/样式修复? → 跳过(使用 /lint)
代码异味(常见触发点):
- 重复代码(相同逻辑出现在多个地方)
- 过长函数(>30行,做太多事)
- 魔法数字/字符串(未解释的字面量)
- 深层嵌套(>3层缩进)
- 死代码(未使用的函数、无法到达的分支)
- 命名不佳(不清楚某物是做什么的)
阶段 1:评估
这真的是重构吗?
| 用户意图 | 操作 |
|---|---|
| “让这个更干净” | ✓ 重构 |
| “添加验证” | ✗ 新行为 → tdd-enforcer |
| “修复这个错误” | ✗ 错误修复 → tdd-enforcer 或 systematic-debugger |
| “格式化这段代码” | ✗ 样式 → /lint |
如果不是重构: 解释并建议正确方法。
阶段 2:保护
代码有测试吗?
| 覆盖率 | 操作 |
|---|---|
| 测试良好 | 跳转到阶段 3 |
| 部分覆盖 | 为未测试部分添加特征测试 |
| 没有测试 | 首先添加特征测试 |
特征测试
在重构前捕获当前行为:
// 特征测试 - 捕获实际行为
it('processOrder 返回当前行为', () => {
const result = processOrder({ items: [], user: null });
// 它现在返回什么,就是期望值
expect(result).toEqual({ status: 'empty', total: 0 });
});
目的: 安全网,而非规范。测试代码“做了什么”,而不是它“应该做什么”。
阶段 3:重构
铁律: 一次只做一个重构。每次更改后运行测试。
重构目录
第 1 层 - 始终安全(不可能改变行为):
| 异味 | 重构方法 | 示例 |
|---|---|---|
| 名称不清晰 | 重命名 | d → discountAmount |
| 函数过长 | 提取函数 | 将 10 行代码提取到 calculateTax() 中 |
| 不必要的变量 | 内联变量 | 移除 temp = x; return temp; |
| 代码位置不当 | 移动函数 | 将 validate() 移动到 Validator 类 |
// ❌ 之前:名称不清晰
const d = price * 0.2;
// ✅ 之后:重命名
const discountAmount = price * 0.2;
第 2 层 - 有测试则安全(如果存在测试则风险低):
| 异味 | 重构方法 | 示例 |
|---|---|---|
| 重复表达式 | 提取变量 | order.items.length > 0 → const hasItems = ... |
| 复杂条件判断 | 分解条件判断 | 将 if 分支提取到命名函数中 |
| 嵌套条件判断 | 卫语句 | 使用提前返回替代深层嵌套 |
| 魔法字面量 | 替换魔法字面量 | 0.2 → VIP_DISCOUNT_RATE |
| 未使用代码 | 移除死代码 | 删除无法到达的分支 |
// ❌ 之前:嵌套条件判断
function getDiscount(user) {
if (user) {
if (user.isVIP) {
return 0.2;
} else {
return 0.1;
}
}
return 0;
}
// ✅ 之后:卫语句
function getDiscount(user) {
if (!user) return 0;
if (user.isVIP) return 0.2;
return 0.1;
}
第 3 层 - 需要谨慎(风险较高,分解为更小步骤):
| 异味 | 重构方法 | 注意事项 |
|---|---|---|
| 上帝类 | 提取类 | 逐步进行,一次移动一个方法 |
| 类型检查条件判断 | 用多态替代 | 需要类层次结构 |
| 参数过多 | 引入参数对象 | 改变函数签名 |
| 复杂循环 | 用管道替代循环 | 确保行为等效 |
决胜规则: 如果多个重构方法适用,首先选择范围最小的(重命名 < 提取变量 < 提取函数 < 提取类)。
阶段 4:验证
每次重构后:
- 运行测试 - 必须通过
- 如果测试通过: 提交,提交信息为
refactor: [更改内容] - 如果测试失败: 立即回滚
回滚协议
git checkout -- <已更改文件>
回滚后:
- 重构步骤太大了吗? → 尝试更小的步骤
- 是否意外改变了行为? → 重新考虑方法
- 不要试图“修复”失败的重构
两次尝试失败后
停止。 询问用户:
“我已尝试此重构两次,但测试持续失败。这表明:
- 重构步骤太大(需要更小的步骤)
- 代码存在隐藏依赖
- 测试脆弱
您希望如何继续?”
阶段 5:迭代
需要更多重构吗?
├─ 是 → 返回阶段 3(再做一次重构)
└─ 否 → 完成
└─ 报告:“重构完成。更改:[摘要]”
边缘情况
部分测试覆盖:
- 识别哪些函数已测试,哪些未测试
- 仅为你即将重构的代码添加特征测试
- 不要试图一口吃成胖子 - 只测试你接触的部分
重构过程中发现错误:
- 停止重构
- 记录错误位置
- 询问用户:“在 X 处发现潜在错误。现在修复(切换到 tdd-enforcer)还是继续重构?”
用户请求大规模重构:
- 分解为步骤:“我将逐步重构。第一步:[步骤 1]”
- 在下一步之前完整完成每一步
- 绝不在一次编辑中批量处理多个重构
反模式
| 不要 | 要 |
|---|---|
| 批量处理多个重构 | 一次重构 → 测试 → 提交 |
| “修复”失败的重构 | 回滚,然后尝试更小的步骤 |
| 在没有测试的情况下重构 | 首先添加特征测试 |
| 在重构过程中改变行为 | 那是功能/修复,不是重构 |
| 跳过提交 | 每次测试通过后提交 |
关键要点
- 一次只做一个更改 - 绝不批量重构
- 重构前先有测试 - 没有安全网 = 不重构
- 失败时回滚 - 不要修复,回滚并重试更小的步骤
- 每次成功后提交 -
refactor: [描述] - 首先处理最小范围 - 重命名 < 提取 < 移动 < 重组