🚀 快速安装

复制以下命令并运行,立即安装此 Skill:

npx @anthropic-ai/skills install github/awesome-copilot/refactor

💡 提示:需要 Node.js 和 NPM

重构

概述

改进代码结构和可读性,同时不改变外部行为。重构是渐进式的演进,而非革命性的重写。用于改进现有代码,而非从头重写。

何时使用

在以下情况下使用该技能:

  • 代码难以理解或维护
  • 函数/类过于庞大
  • 需要处理代码坏味道
  • 由于代码结构导致难以添加新功能
  • 用户要求”清理这段代码”、”重构这个”、”改进这个”

重构原则

黄金法则

  1. 行为保持不变 – 重构不改变代码的功能,只改变其实现方式
  2. 小步快跑 – 进行微小修改,每次修改后都进行测试
  3. 版本控制是你的朋友 – 在每个安全状态前后提交
  4. 测试至关重要 – 没有测试,就不是重构,只是在编辑
  5. 一次只做一件事 – 不要将重构与功能变更混在一起

何时不应重构

- 代码能正常工作且不会再变更(如果没坏...)
- 没有测试的关键生产代码(先添加测试)
- 时间紧迫时
- "仅仅因为想重构" - 需要有明确目的

常见代码坏味道及修复

1. 长方法/函数

# 坏:200 行的函数什么都做
- async function processOrder(orderId) {
-   // 50 行:获取订单
-   // 30 行:验证订单
-   // 40 行:计算价格
-   // 30 行:更新库存
-   // 20 行:创建发货单
-   // 30 行:发送通知
- }

# 好:拆分为专注的函数
+ async function processOrder(orderId) {
+   const order = await fetchOrder(orderId);
+   validateOrder(order);
+   const pricing = calculatePricing(order);
+   await updateInventory(order);
+   const shipment = await createShipment(order);
+   await sendNotifications(order, pricing, shipment);
+   return { order, pricing, shipment };
+ }

2. 重复代码

# 坏:多处有相同逻辑
- function calculateUserDiscount(user) {
-   if (user.membership === 'gold') return user.total * 0.2;
-   if (user.membership === 'silver') return user.total * 0.1;
-   return 0;
- }
-
- function calculateOrderDiscount(order) {
-   if (order.user.membership === 'gold') return order.total * 0.2;
-   if (order.user.membership === 'silver') return order.total * 0.1;
-   return 0;
- }

# 好:提取公共逻辑
+ function getMembershipDiscountRate(membership) {
+   const rates = { gold: 0.2, silver: 0.1 };
+   return rates[membership] || 0;
+ }
+
+ function calculateUserDiscount(user) {
+   return user.total * getMembershipDiscountRate(user.membership);
+ }
+
+ function calculateOrderDiscount(order) {
+   return order.total * getMembershipDiscountRate(order.user.membership);
+ }

3. 大类/大模块

# 坏:上帝对象,知道的太多
- class UserManager {
-   createUser() { /* ... */ }
-   updateUser() { /* ... */ }
-   deleteUser() { /* ... */ }
-   sendEmail() { /* ... */ }
-   generateReport() { /* ... */ }
-   handlePayment() { /* ... */ }
-   validateAddress() { /* ... */ }
-   // 还有 50 多个方法...
- }

# 好:每个类单一职责
+ class UserService {
+   create(data) { /* ... */ }
+   update(id, data) { /* ... */ }
+   delete(id) { /* ... */ }
+ }
+
+ class EmailService {
+   send(to, subject, body) { /* ... */ }
+ }
+
+ class ReportService {
+   generate(type, params) { /* ... */ }
+ }
+
+ class PaymentService {
+   process(amount, method) { /* ... */ }
+ }

4. 长参数列表

# 坏:参数太多
- function createUser(email, password, name, age, address, city, country, phone) {
-   /* ... */
- }

# 好:将相关参数分组
+ interface UserData {
+   email: string;
+   password: string;
+   name: string;
+   age?: number;
+   address?: Address;
+   phone?: string;
+ }
+
+ function createUser(data: UserData) {
+   /* ... */
+ }

# 甚至更好:对复杂构建使用建造者模式
+ const user = UserBuilder
+   .email('test@example.com')
+   .password('secure123')
+   .name('Test User')
+   .address(address)
+   .build();

5. 依恋情结

# 坏:方法使用另一个对象的数据多于自己的
- class Order {
-   calculateDiscount(user) {
-     if (user.membershipLevel === 'gold') {
+       return this.total * 0.2;
+     }
+     if (user.accountAge > 365) {
+       return this.total * 0.1;
+     }
+     return 0;
+   }
+ }

# 好:将逻辑移到拥有数据的对象中
+ class User {
+   getDiscountRate(orderTotal) {
+     if (this.membershipLevel === 'gold') return 0.2;
+     if (this.accountAge > 365) return 0.1;
+     return 0;
+   }
+ }
+
+ class Order {
+   calculateDiscount(user) {
+     return this.total * user.getDiscountRate(this.total);
+   }
+ }

6. 基本类型偏执

# 坏:对领域概念使用基本类型
- function sendEmail(to, subject, body) { /* ... */ }
- sendEmail('user@example.com', 'Hello', '...');

- function createPhone(country, number) {
-   return `${country}-${number}`;
- }

# 好:使用领域类型
+ class Email {
+   private constructor(public readonly value: string) {
+     if (!Email.isValid(value)) throw new Error('无效的邮箱');
+   }
+   static create(value: string) { return new Email(value); }
+   static isValid(email: string) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); }
+ }
+
+ class PhoneNumber {
+   constructor(
+     public readonly country: string,
+     public readonly number: string
+   ) {
+     if (!PhoneNumber.isValid(country, number)) throw new Error('无效的电话');
+   }
+   toString() { return `${this.country}-${this.number}`; }
+   static isValid(country: string, number: string) { /* ... */ }
+ }
+
+ // 使用
+ const email = Email.create('user@example.com');
+ const phone = new PhoneNumber('1', '555-1234');

7. 魔法数字/字符串

# 坏:未解释的值
- if (user.status === 2) { /* ... */ }
- const discount = total * 0.15;
- setTimeout(callback, 86400000);

# 好:命名常量
+ const UserStatus = {
+   ACTIVE: 1,
+   INACTIVE: 2,
+   SUSPENDED: 3
+ } as const;
+
+ const DISCOUNT_RATES = {
+   STANDARD: 0.1,
+   PREMIUM: 0.15,
+   VIP: 0.2
+ } as const;
+
+ const ONE_DAY_MS = 24 * 60 * 60 * 1000;
+
+ if (user.status === UserStatus.INACTIVE) { /* ... */ }
+ const discount = total * DISCOUNT_RATES.PREMIUM;
+ setTimeout(callback, ONE_DAY_MS);

8. 嵌套条件

# 坏:箭头形代码
- function process(order) {
-   if (order) {
-     if (order.user) {
-       if (order.user.isActive) {
-         if (order.total > 0) {
-           return processOrder(order);
+         } else {
+           return { error: '无效的总金额' };
+         }
+       } else {
+         return { error: '用户未激活' };
+       }
+     } else {
+       return { error: '没有用户' };
+     }
+   } else {
+     return { error: '没有订单' };
+   }
+ }

# 好:卫语句 / 提前返回
+ function process(order) {
+   if (!order) return { error: '没有订单' };
+   if (!order.user) return { error: '没有用户' };
+   if (!order.user.isActive) return { error: '用户未激活' };
+   if (order.total <= 0) return { error: '无效的总金额' };
+   return processOrder(order);
+ }

# 甚至更好:使用 Result 类型
+ function process(order): Result {
+   return Result.combine([
+     validateOrderExists(order),
+     validateUserExists(order),
+     validateUserActive(order.user),
+     validateOrderTotal(order)
+   ]).flatMap(() => processOrder(order));
+ }

9. 死代码

# 坏:未使用的代码残留
- function oldImplementation() { /* ... */ }
- const DEPRECATED_VALUE = 5;
- import { unusedThing } from './somewhere';
- // 被注释掉的代码
- // function oldCode() { /* ... */ }

# 好:删除它
+ // 删除未使用的函数、导入和注释掉的代码
+ // 如果再次需要,git 历史中有

10. 过度亲密

# 坏:一个类深入访问另一个类的内部
- class OrderProcessor {
-   process(order) {
-     order.user.profile.address.street;  // 过于亲密
-     order.repository.connection.config;  // 破坏封装
+   }
+ }

# 好:请求而非告知
+ class OrderProcessor {
+   process(order) {
+     order.getShippingAddress();  // 订单知道如何获取
+     order.save();  // 订单知道如何保存自己
+   }
+ }

提取方法重构

前后对比

# 之前:一个长函数
- function printReport(users) {
-   console.log('用户报告');
-   console.log('============');
-   console.log('');
-   console.log(`总用户数: ${users.length}`);
-   console.log('');
-   console.log('活跃用户');
-   console.log('------------');
-   const active = users.filter(u => u.isActive);
-   active.forEach(u => {
-     console.log(`- ${u.name} (${u.email})`);
-   });
-   console.log('');
-   console.log(`活跃: ${active.length}`);
-   console.log('');
-   console.log('非活跃用户');
-   console.log('--------------');
-   const inactive = users.filter(u => !u.isActive);
-   inactive.forEach(u => {
-     console.log(`- ${u.name} (${u.email})`);
-   });
-   console.log('');
-   console.log(`非活跃: ${inactive.length}`);
- }

# 之后:提取出的方法
+ function printReport(users) {
+   printHeader('用户报告');
+   console.log(`总用户数: ${users.length}\n`);
+   printUserSection('活跃用户', users.filter(u => u.isActive));
+   printUserSection('非活跃用户', users.filter(u => !u.isActive));
+ }
+
+ function printHeader(title) {
+   const line = '='.repeat(title.length);
+   console.log(title);
+   console.log(line);
+   console.log('');
+ }
+
+ function printUserSection(title, users) {
+   console.log(title);
+   console.log('-'.repeat(title.length));
+   users.forEach(u => console.log(`- ${u.name} (${u.email})`));
+   console.log('');
+   console.log(`${title.split(' ')[0]}: ${users.length}`);
+   console.log('');
+ }

引入类型安全

从无类型到有类型

# 之前:无类型
- function calculateDiscount(user, total, membership, date) {
-   if (membership === 'gold' && date.getDay() === 5) {
-     return total * 0.25;
-   }
-   if (membership === 'gold') return total * 0.2;
-   return total * 0.1;
- }

# 之后:完全类型安全
+ type Membership = 'bronze' | 'silver' | 'gold';
+
+ interface User {
+   id: string;
+   name: string;
+   membership: Membership;
+ }
+
+ interface DiscountResult {
+   original: number;
+   discount: number;
+   final: number;
+   rate: number;
+ }
+
+ function calculateDiscount(
+   user: User,
+   total: number,
+   date: Date = new Date()
+ ): DiscountResult {
+   if (total < 0) throw new Error('总金额不能为负');
+
+   let rate = 0.1; // 默认青铜
+
+   if (user.membership === 'gold' && date.getDay() === 5) {
+     rate = 0.25; // 黄金会员周五奖励
+   } else if (user.membership === 'gold') {
+     rate = 0.2;
+   } else if (user.membership === 'silver') {
+     rate = 0.15;
+   }
+
+   const discount = total * rate;
+
+   return {
+     original: total,
+     discount,
+     final: total - discount,
+     rate
+   };
+ }

用于重构的设计模式

策略模式

# 之前:条件逻辑
- function calculateShipping(order, method) {
-   if (method === 'standard') {
-     return order.total > 50 ? 0 : 5.99;
-   } else if (method === 'express') {
-     return order.total > 100 ? 9.99 : 14.99;
+   } else if (method === 'overnight') {
+     return 29.99;
+   }
+ }

# 之后:策略模式
+ interface ShippingStrategy {
+   calculate(order: Order): number;
+ }
+
+ class StandardShipping implements ShippingStrategy {
+   calculate(order: Order) {
+     return order.total > 50 ? 0 : 5.99;
+   }
+ }
+
+ class ExpressShipping implements ShippingStrategy {
+   calculate(order: Order) {
+     return order.total > 100 ? 9.99 : 14.99;
+   }
+ }
+
+ class OvernightShipping implements ShippingStrategy {
+   calculate(order: Order) {
+     return 29.99;
+   }
+ }
+
+ function calculateShipping(order: Order, strategy: ShippingStrategy) {
+   return strategy.calculate(order);
+ }

责任链模式

# 之前:嵌套验证
- function validate(user) {
-   const errors = [];
-   if (!user.email) errors.push('邮箱是必填项');
+   else if (!isValidEmail(user.email)) errors.push('无效的邮箱格式');
+   if (!user.name) errors.push('姓名是必填项');
+   if (user.age < 18) errors.push('必须年满18岁');
+   if (user.country === 'blocked') errors.push('不支持该国家/地区');
+   return errors;
+ }

# 之后:责任链模式
+ abstract class Validator {
+   abstract validate(user: User): string | null;
+   setNext(validator: Validator): Validator {
+     this.next = validator;
+     return validator;
+   }
+   validate(user: User): string | null {
+     const error = this.doValidate(user);
+     if (error) return error;
+     return this.next?.validate(user) ?? null;
+   }
+ }
+
+ class EmailRequiredValidator extends Validator {
+   doValidate(user: User) {
+     return !user.email ? '邮箱是必填项' : null;
+   }
+ }
+
+ class EmailFormatValidator extends Validator {
+   doValidate(user: User) {
+     return user.email && !isValidEmail(user.email) ? '无效的邮箱格式' : null;
+   }
+ }
+
+ // 构建责任链
+ const validator = new EmailRequiredValidator()
+   .setNext(new EmailFormatValidator())
+   .setNext(new NameRequiredValidator())
+   .setNext(new AgeValidator())
+   .setNext(new CountryValidator());

重构步骤

安全重构流程

1. 准备
   - 确保有测试(如果没有,先编写)
   - 提交当前状态
   - 创建功能分支

2. 识别
   - 找到需要处理的代码坏味道
   - 理解代码的功能
   - 规划重构

3. 重构(小步快跑)
   - 做一个小改动
   - 运行测试
   - 如果测试通过,提交
   - 重复

4. 验证
   - 所有测试通过
   - 如果需要,进行手动测试
   - 性能不变或提升

5. 清理
   - 更新注释
   - 更新文档
   - 最终提交

重构检查清单

代码质量

  • 函数短小(< 50 行)
  • 函数只做一件事
  • 无重复代码
  • 描述性命名(变量、函数、类)
  • 无魔法数字/字符串
  • 死代码已移除

结构

  • 相关代码放在一起
  • 模块边界清晰
  • 依赖单向流动
  • 无循环依赖

类型安全

  • 所有公共 API 定义了类型
  • 无未经说明的 any 类型
  • 可空类型已明确标记

测试

  • 重构后的代码已测试
  • 测试覆盖边界情况
  • 所有测试通过

常见重构操作

操作 描述
提取方法 将代码片段转为方法
提取类 将行为移至新类
提取接口 从实现创建接口
内联方法 将方法体移回调用处
内联类 将类行为移至调用处
上移方法 将方法移至父类
下移方法 将方法移至子类
重命名方法/变量 提高清晰度
引入参数对象 将相关参数分组
用多态替换条件 用多态替代 switch/if
用常量替换魔法数字 命名常量
分解条件表达式 拆分复杂条件
合并条件表达式 合并重复条件
用卫语句替换嵌套条件 提前返回
引入空对象 消除空值检查
用类/枚举替换类型码 强类型
用委托替换继承 组合优于继承

📄 原始文档

完整文档(英文):

https://skills.sh/github/awesome-copilot/refactor

💡 提示:点击上方链接查看 skills.sh 原始英文文档,方便对照翻译。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。