name: 重构 description: | 在不改变外部行为的情况下,重构现有代码以提高可读性、可维护性和性能。
使用时机:在不改变行为的情况下重构代码,提取方法/类,移除重复,应用设计模式,改进代码组织,减少技术债务。 不要使用:用于修复错误(使用 /debugging),用于添加测试(使用 /testing),用于新功能(直接实现)。
触发词:重构、重组、重写、清理、简化、提取、内联、重命名、移动、拆分、合并、分解、模块化、解耦、技术债务、代码异味、DRY、SOLID、改进代码、现代化、重组。 triggers:
- 重构
- 重组
- 重写
- 清理
- 简化
- 提取
- 内联
- 重命名
- 移动
- 拆分
- 合并
- 分解
- 模块化
- 解耦
- 技术债务
- 代码异味
- DRY
- SOLID
- 改进代码
- 现代化
- 重组 allowed-tools: 读取、查找、匹配、编辑、写入、命令行
重构
概述
此技能专注于通过系统性重构技术提高代码质量。它识别代码异味并应用已验证的重构模式,在保留功能的同时增强可维护性。
当用户请求以下内容时使用此技能:
- 代码重构或清理
- 减少技术债务
- 改进代码组织
- 应用设计模式
- 拆分大型文件或函数
- 移除重复
- 现代化遗留代码
- 改进可测试性
范围选择
何时使用此技能:
- 局部重构(单个模块/文件)
- 模式应用(提取方法、引入接口)
- 测试重构和测试套件改进
- 数据流水线重组
- 具有测试覆盖的安全重构
何时升级到高级软件工程师:
- 影响多个模块的架构变更
- 需要迁移路径的API重新设计
- 范围不清晰的跨组件重构
- 需要性能分析的性能优化
- 基础设施重构(使用基础设施相关技能,如 /terraform、/kubernetes)
- 安全性敏感的重构(使用 /security-audit 和 /threat-model 技能)
指令
1. 识别重构机会
要搜索的代码异味:
- 长方法(>50行)或大类(>300行)
- 重复的代码块
- 高圈复杂度
- 魔法数字和字符串字面量
- 深度嵌套(>3层)
- 特性嫉妒(方法使用另一个类的数据)
- 霰弹枪式手术(一个变更需要多次编辑)
- 数据簇(相同的参数组)
- 原始类型执着(使用原始类型而非对象)
使用查找/匹配来查找模式:
# 查找长函数(粗略启发式)
rg "^(\s*)(def|function|fn|func)\s+\w+" --after-context=60
# 查找重复代码
rg --multiline "pattern.*
.*pattern"
# 查找魔法数字
rg "\b\d{2,}\b" --type=py --type=js --type=rs
2. 规划重构
开始前:
- 验证测试覆盖 - 运行测试建立基线
- 列出所有变更 - 记录重构序列
- 识别依赖关系 - 什么代码依赖于您要更改的代码?
- 规划回滚点 - 哪里可以安全提交?
- 检查影响 - 查找对正在更改的函数/类的引用
需要升级的红色标志:
- 不存在测试覆盖
- 变更影响具有外部消费者的公共API
- 所有权不清晰或涉及多个团队
- 性能关键的热路径
3. 应用重构模式
代码组织模式
提取方法/函数:
- 将长函数拆分为更小、专注的函数
- 每个函数应做一件事
- 提高可读性和可测试性
提取类:
- 拆分具有多个职责的大类
- 遵循单一职责原则
- 提高内聚性并减少耦合
移动方法/字段:
- 将方法移动到使用其数据的类中
- 减少特性嫉妒
- 提高封装性
内联方法/变量:
- 移除不必要的间接
- 简化过度抽象的代码
- 当抽象不增加价值时使用
重命名:
- 提高命名清晰度
- 使用领域语言
- 使意图明确
结构模式
用多态替换条件:
- 用子类方法替换类型切换
- 实现开放/封闭原则
- 提高可扩展性
引入参数对象:
- 将相关参数分组到对象中
- 减少参数列表
- 使数据关系明确
用常量替换魔法数字:
- 为字面量定义命名常量
- 提高可读性和可维护性
- 集中配置
分解条件:
- 将复杂条件提取到命名函数中
- 用防护子句替换嵌套if
- 提高可读性
测试重构模式
提取测试固件:
- 将通用设置移动到固件/工厂中
- 减少测试文件中的重复
- 提高测试可维护性
引入测试数据构建器:
- 用构建器替换复杂的对象构建
- 使测试意图更清晰
- 简化测试设置
替换断言轮盘:
- 使用描述性断言消息
- 每个测试一个逻辑断言
- 清晰的失败消息
提取测试助手:
- 将重复的测试逻辑移动到助手中
- 保持测试专注于行为
- 提高测试可读性
数据流水线模式
提取转换:
- 隔离数据转换逻辑
- 使转换可组合
- 提高可测试性
引入流水线接口:
- 定义标准输入/输出契约
- 启用阶段组合
- 简化测试和调试
用阶段替换内联处理:
- 将整体处理拆分为阶段
- 每个阶段具有单一职责
- 启用并行化和监控
4. 安全重构工作流
对于每个重构步骤:
- 进行变更 - 一次一个重构
- 运行测试 - 验证行为被保留
- 提交 - 使用清晰消息创建检查点
- 重复 - 移动到下一个重构
如果测试失败:
- 立即恢复(git checkout)
- 分析失败 - 是测试问题还是行为变更?
- 修复或调整方法
提交消息格式:
重构:从process_order中提取calculate_discount
通过隔离折扣逻辑提高可测试性。
行为无变更。
5. 验证变更
验证检查表:
- 所有测试通过(现有 + 任何新测试)
- 功能无回归
- 性能未降级(对于热路径)
- 代码覆盖保持或提高
- 代码检查通过
- 构建成功
对于大型重构:
- 运行额外的冒烟测试
- 检查内存使用(如适用)
- 审查错误处理保留情况
最佳实践
- 小步前进:进行增量变更,而不是大爆炸重写
- 测试先行:确保在重构前有测试 - 如果缺失则编写
- 一次一件事:每个提交专注于单个重构
- 保留行为:外部行为必须保持不变
- 保持工作状态:每个步骤后代码应通过测试
- 记录意图:在提交中解释为什么需要重构
- 重构测试:保持测试清洁和可维护
- 使用类型安全:利用类型系统早期捕获错误
- 测量而非猜测:在优化性能前进行性能分析
- 知道何时停止:不要过度设计或添加不必要的抽象
常见模式示例
模式 1:提取方法
# 之前:具有多个职责的长方法
def process_order(order):
# 验证订单
if not order.items:
raise ValueError("订单为空")
if order.total < 0:
raise ValueError("无效总额")
# 计算折扣
discount = 0
if order.customer.is_premium:
discount = order.total * 0.1
if order.total > 1000:
discount += order.total * 0.05
# 应用折扣并保存
order.final_total = order.total - discount
order.save()
# 之后:提取的方法,具有单一职责
def process_order(order):
validate_order(order)
discount = calculate_discount(order)
finalize_order(order, discount)
def validate_order(order):
if not order.items:
raise ValueError("订单为空")
if order.total < 0:
raise ValueError("无效总额")
def calculate_discount(order) -> float:
discount = 0
if order.customer.is_premium:
discount = order.total * 0.1
if order.total > 1000:
discount += order.total * 0.05
return discount
def finalize_order(order, discount: float):
order.final_total = order.total - discount
order.save()
模式 2:用常量替换魔法数字
// 之前
if (response.status === 200) {
setTimeout(retry, 3000);
if (attempts > 5) {
throw new Error("超过最大重试次数");
}
}
// 之后
const HTTP_OK = 200;
const RETRY_DELAY_MS = 3000;
const MAX_RETRY_ATTEMPTS = 5;
if (response.status === HTTP_OK) {
setTimeout(retry, RETRY_DELAY_MS);
if (attempts > MAX_RETRY_ATTEMPTS) {
throw new Error("超过最大重试次数");
}
}
模式 3:用防护子句替换嵌套条件
# 之前
def get_payment_amount(employee):
if employee.is_active:
if employee.is_full_time:
if employee.tenure > 5:
return employee.salary * 1.1
else:
return employee.salary
else:
return employee.hourly_rate * employee.hours
else:
return 0
# 之后
def get_payment_amount(employee):
if not employee.is_active:
return 0
if not employee.is_full_time:
return employee.hourly_rate * employee.hours
if employee.tenure > 5:
return employee.salary * 1.1
return employee.salary
模式 4:引入参数对象
// 之前
function createUser(
firstName: string,
lastName: string,
email: string,
phone: string,
street: string,
city: string,
state: string,
zip: string,
) {
// ...
}
// 之后
interface UserDetails {
name: PersonName;
contact: ContactInfo;
address: Address;
}
interface PersonName {
first: string;
last: string;
}
interface ContactInfo {
email: string;
phone: string;
}
interface Address {
street: string;
city: string;
state: string;
zip: string;
}
function createUser(details: UserDetails) {
// ...
}
模式 5:用多态替换类型代码
// 之前
enum ShapeType { Circle, Rectangle, Triangle }
struct Shape {
shape_type: ShapeType,
radius: f64,
width: f64,
height: f64,
base: f64,
}
impl Shape {
fn area(&self) -> f64 {
match self.shape_type {
ShapeType::Circle => 3.14159 * self.radius * self.radius,
ShapeType::Rectangle => self.width * self.height,
ShapeType::Triangle => 0.5 * self.base * self.height,
}
}
}
// 之后
trait Shape {
fn area(&self) -> f64;
}
struct Circle { radius: f64 }
struct Rectangle { width: f64, height: f64 }
struct Triangle { base: f64, height: f64 }
impl Shape for Circle {
fn area(&self) -> f64 {
3.14159 * self.radius * self.radius
}
}
impl Shape for Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}
impl Shape for Triangle {
fn area(&self) -> f64 {
0.5 * self.base * self.height
}
}
模式 6:提取数据流水线阶段
# 之前:整体处理
def process_data(raw_data):
# 验证
if not raw_data:
raise ValueError("数据为空")
# 清理
cleaned = []
for item in raw_data:
if item.get('status') == 'valid':
cleaned.append(item)
# 转换
transformed = []
for item in cleaned:
transformed.append({
'id': item['id'],
'value': item['raw_value'] * 100,
'timestamp': item['ts']
})
# 聚合
total = sum(item['value'] for item in transformed)
return {'items': transformed, 'total': total}
# 之后:可组合阶段的流水线
def validate_input(raw_data):
if not raw_data:
raise ValueError("数据为空")
return raw_data
def filter_valid(data):
return [item for item in data if item.get('status') == 'valid']
def transform_items(data):
return [
{
'id': item['id'],
'value': item['raw_value'] * 100,
'timestamp': item['ts']
}
for item in data
]
def aggregate_results(transformed):
total = sum(item['value'] for item in transformed)
return {'items': transformed, 'total': total}
def process_data(raw_data):
return (raw_data
|> validate_input
|> filter_valid
|> transform_items
|> aggregate_results)
模式 7:使用构建器简化测试
// 之前
func TestUserRegistration(t *testing.T) {
user := &User{
FirstName: "John",
LastName: "Doe",
Email: "john@example.com",
Phone: "555-0100",
Address: Address{
Street: "123 Main St",
City: "Springfield",
State: "IL",
Zip: "62701",
},
Preferences: Preferences{
NewsletterEnabled: true,
Theme: "dark",
},
CreatedAt: time.Now(),
}
err := RegisterUser(user)
assert.NoError(t, err)
}
// 之后
func TestUserRegistration(t *testing.T) {
user := NewUserBuilder().
WithName("John", "Doe").
WithEmail("john@example.com").
Build()
err := RegisterUser(user)
assert.NoError(t, err)
}
// 测试构建器专注于每个测试重要的部分
func TestUserWithNewsletter(t *testing.T) {
user := NewUserBuilder().WithNewsletter(true).Build()
// ...
}
何时停止重构
停止如果:
- 测试开始意外失败
- 范围超出初始计划
- 不确定架构影响
- 出现性能担忧
- 安全性影响不清晰
在这些情况下:
- 提交当前进展
- 记录剩余工作
- 升级到高级软件工程师或相关专家