🚀 快速安装
复制以下命令并运行,立即安装此 Skill:
npx @anthropic-ai/skills install github/awesome-copilot/refactor
💡 提示:需要 Node.js 和 NPM
重构
概述
改进代码结构和可读性,同时不改变外部行为。重构是渐进式的演进,而非革命性的重写。用于改进现有代码,而非从头重写。
何时使用
在以下情况下使用该技能:
- 代码难以理解或维护
- 函数/类过于庞大
- 需要处理代码坏味道
- 由于代码结构导致难以添加新功能
- 用户要求”清理这段代码”、”重构这个”、”改进这个”
重构原则
黄金法则
- 行为保持不变 – 重构不改变代码的功能,只改变其实现方式
- 小步快跑 – 进行微小修改,每次修改后都进行测试
- 版本控制是你的朋友 – 在每个安全状态前后提交
- 测试至关重要 – 没有测试,就不是重构,只是在编辑
- 一次只做一件事 – 不要将重构与功能变更混在一起
何时不应重构
- 代码能正常工作且不会再变更(如果没坏...)
- 没有测试的关键生产代码(先添加测试)
- 时间紧迫时
- "仅仅因为想重构" - 需要有明确目的
常见代码坏味道及修复
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 原始英文文档,方便对照翻译。
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

评论(0)