名称: 代码重构模式 描述: 系统化重构代码的方法,用于提高可维护性、性能和清晰度,同时保持功能不变 许可证: MIT 元数据: 改编者: ai-skills 类别: 代码质量
代码重构模式
一个全面的指南,用于系统化地重构代码,同时保持功能并提高质量。
何时重构
- 检测到代码异味(重复代码、长函数等)
- 在向复杂区域添加新功能之前
- 理解改进后(“现在我看到了更好的方法”)
- 当测试就位时
- 需要性能优化时
重构规则
- 永远不要在没有测试的情况下重构:如果测试不存在,先写测试
- 小步前进:一次只做一个更改
- 每次更改后运行测试:确保没有破坏任何东西
- 经常提交:每个工作重构都是一个提交
- 不要将重构与功能工作混在一起:分开关注点
常见代码异味
1. 长方法/函数
异味:函数超过20-30行
重构:提取方法
// 之前
function processOrder(order: Order) {
// 验证订单 (10 lines)
// 计算总计 (15 lines)
// 应用折扣 (12 lines)
// 发送确认 (8 lines)
}
// 之后
function processOrder(order: Order) {
validateOrder(order);
const totals = calculateTotals(order);
const finalPrice = applyDiscounts(totals, order);
sendConfirmation(order, finalPrice);
}
2. 重复代码
异味:相同代码出现在多个地方
重构:提取函数/类
// 之前
function formatUserName(user: User) {
return `${user.firstName} ${user.lastName}`;
}
function formatAuthorName(author: Author) {
return `${author.firstName} ${author.lastName}`;
}
// 之后
function formatFullName(person: { firstName: string; lastName: string }) {
return `${person.firstName} ${person.lastName}`;
}
3. 长参数列表
异味:函数有4个以上参数
重构:参数对象
// 之前
function createUser(
firstName: string,
lastName: string,
email: string,
phone: string,
address: string
) { }
// 之后
interface UserDetails {
firstName: string;
lastName: string;
email: string;
phone: string;
address: string;
}
function createUser(details: UserDetails) { }
4. 大类
异味:类有许多责任
重构:提取类
// 之前
class UserManager {
createUser() { }
deleteUser() { }
sendEmail() { }
generateReport() { }
logActivity() { }
}
// 之后
class UserService {
createUser() { }
deleteUser() { }
}
class EmailService {
sendEmail() { }
}
class ReportService {
generateReport() { }
}
5. 特征羡慕
异味:方法使用另一个类的数据多于自己类的数据
重构:移动方法
// 之前
class Order {
calculate() {
return this.customer.getDiscount() * this.amount;
}
}
// 之后
class Customer {
calculateOrderAmount(order: Order) {
return this.getDiscount() * order.amount;
}
}
重构技术
提取方法
将大函数分解为更小、有名称的部分:
// 之前
function renderUser(user: User) {
console.log(`<div>`);
console.log(` <h1>${user.firstName} ${user.lastName}</h1>`);
console.log(` <p>${user.email}</p>`);
console.log(`</div>`);
}
// 之后
function renderUser(user: User) {
console.log(`<div>`);
console.log(` ${renderUserHeader(user)}`);
console.log(` ${renderUserEmail(user)}`);
console.log(`</div>`);
}
function renderUserHeader(user: User) {
return `<h1>${user.firstName} ${user.lastName}</h1>`;
}
function renderUserEmail(user: User) {
return `<p>${user.email}</p>`;
}
重命名以清晰化
使用描述性名称:
// 之前
function calc(a: number, b: number) {
return a * b * 0.08;
}
// 之后
function calculateSalesTax(amount: number, quantity: number) {
const TAX_RATE = 0.08;
return amount * quantity * TAX_RATE;
}
引入解释性变量
使复杂表达式清晰:
// 之前
if (platform.toUpperCase().includes('MAC') &&
browser.toUpperCase().includes('IE') &&
wasInitialized() && resized) {
// 做某事
}
// 之后
const isMacOS = platform.toUpperCase().includes('MAC');
const isIE = browser.toUpperCase().includes('IE');
const wasResized = wasInitialized() && resized;
if (isMacOS && isIE && wasResized) {
// 做某事
}
用多态替换条件
使用继承/接口代替switch/if-else链:
// 之前
function getSpeed(vehicle: Vehicle) {
switch (vehicle.type) {
case 'car': return vehicle.speed * 1.0;
case 'bike': return vehicle.speed * 0.8;
case 'truck': return vehicle.speed * 0.6;
}
}
// 之后
interface Vehicle {
getSpeed(): number;
}
class Car implements Vehicle {
getSpeed() { return this.speed * 1.0; }
}
class Bike implements Vehicle {
getSpeed() { return this.speed * 0.8; }
}
简化条件逻辑
使用早期返回和守卫子句:
// 之前
function processPayment(payment: Payment) {
if (payment.isValid()) {
if (payment.amount > 0) {
if (payment.method === 'card') {
// 处理卡片支付
} else {
// 无效方法
}
} else {
// 无效金额
}
} else {
// 无效支付
}
}
// 之后
function processPayment(payment: Payment) {
if (!payment.isValid()) {
throw new Error('无效支付');
}
if (payment.amount <= 0) {
throw new Error('无效金额');
}
if (payment.method !== 'card') {
throw new Error('无效方法');
}
// 处理卡片支付
}
重构工作流
步骤1:理解当前代码
# 彻底阅读代码
cat src/feature.ts
# 检查测试
cat src/feature.test.ts
# 查找所有使用情况
grep -r "functionName" src/
步骤2:确保测试存在
# 运行现有测试
npm test src/feature.test.ts
# 如果需要,添加缺失测试
步骤3:小步重构
# 做一个重构更改
# 运行测试
npm test
# 如果测试通过,提交
git add . && git commit -m "重构:提取方法 calculateTotal"
# 重复下一个重构
步骤4:验证无回归
# 运行完整测试套件
npm test
# 检查类型错误
npx tsc --noEmit
# 验证代码检查
npm run lint
步骤5:性能检查
# 如果性能关键,比较前后性能
npm run benchmark
重构检查清单
重构前:
- [ ] 测试存在并通过
- [ ] 理解当前行为
- [ ] 知道为什么需要重构
- [ ] 有时间完成重构
重构中:
- [ ] 一次只做一个更改
- [ ] 每次更改后运行测试
- [ ] 保持提交小而专注
- [ ] 重构期间不要添加功能
重构后:
- [ ] 所有测试通过
- [ ] 无类型错误
- [ ] 代码检查通过
- [ ] 完成代码审查
- [ ] 如果需要,更新文档
集成点
补充:
- 验证循环:用于重构后的验证
- TDD工作流:用于测试优先方法
- 编码标准强制执行器:用于样式一致性
- 测试模式:用于测试设计
重构反模式
❌ 不要:
- 没有测试的情况下重构
- 将重构与功能工作混在一起
- 一次做大的更改
- 重构你不理解的代码
- 跳过验证步骤
✅ 做:
- 先写测试
- 分开重构提交
- 做增量更改
- 重构前理解代码
- 频繁运行测试