mcpField
R
refactoring

refactoring

github.com/mgd34msu开发659 次安装10 周前更新
代码仓库

关于 refactoring

refactoring 是面向开发者的代码重构技能,强调在保持外部行为不变的前提下,系统地改善代码结构与可维护性。内容涵盖抽取、重命名、类型改进和依赖解耦。

主要功能

  1. 01分析代码异味与依赖关系
  2. 02拆分函数和组件
  3. 03整理命名与文件结构
  4. 04简化条件逻辑
  5. 05改进类型并解耦依赖

适用场景

  1. 01消除重复代码
  2. 02拆解过长的函数或组件
  3. 03降低 TypeScript 中 any 的使用
  4. 04整理复杂条件与文件布局

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mgd34msu/goodvibes-plugin --skill refactoring

安装后在 Claude Code 中输入/refactoring确认已加载。

附带文件references/refactoring-patterns.mdscripts/validate-refactoring.sh

详细介绍

资源

scripts/
  validate-refactoring.sh
references/
  refactoring-patterns.md

重构质量技能

此技能将教你如何使用 GoodVibes 精准工具安全、系统地重构代码。重构可以在不改变外部行为的情况下改进代码结构和可维护性,加快未来的开发速度并减少错误。

何时使用此技能

在以下情况下加载此技能:

  • 需要消除代码重复
  • 函数或组件变得过于庞大
  • 需要改进类型安全(减少 any 的使用)
  • 条件逻辑变得复杂且难以理解
  • 需要重新组织文件和文件夹结构
  • 依赖关系紧密耦合,需要反转依赖
  • 需要优化数据库架构或查询
  • 代码异味正在拖慢开发

触发语句:“重构这段代码”“减少重复”“提取函数”“简化条件逻辑”“改进类型”“重新组织”“清理代码”。

核心工作流

阶段 1:分析——了解当前状态

重构之前,先梳理当前代码结构并找出重构机会。

步骤 1.1:识别代码异味

使用 discover 找出需要重构的常见代码异味。

discover:
  queries:
    # Large files (> 300 lines)
    - id: large_files
      type: glob
      patterns: ["src/**/*.{ts,tsx,js,jsx}"]
    # Code duplication
    - id: duplicate_patterns
      type: grep
      pattern: "(function|const|class)\\s+\\w+"
      glob: "**/*.{ts,tsx,js,jsx}"
    # Type safety issues
    - id: any_usage
      type: grep
      pattern: ":\\s*any(\\s|;|,|\\))"
      glob: "**/*.{ts,tsx}"
    # Complex conditionals
    - id: nested_conditions
      type: grep
      pattern: "if\\s*\\(.*if\\s*\\("
      glob: "**/*.{ts,tsx,js,jsx}"
  verbosity: files_only

这会揭示:

  • 需要拆分的文件
  • 重复的代码模式
  • 类型安全方面的不足
  • 复杂的条件逻辑
步骤 1.2:了解依赖关系

使用 precision_grep 梳理代码在整个代码库中的使用情况。

precision_grep:
  queries:
    - id: function_usage
      pattern: "importFunctionName\\("
      glob: "**/*.{ts,tsx,js,jsx}"
  output:
    format: locations
  verbosity: standard

这很重要,因为:

  • 重构需要更新所有调用点
  • 必须提前识别会造成破坏的更改
  • 可以安全地移除未使用的代码
步骤 1.3:确保测试存在

只有在测试能够验证行为保持不变时,重构才是安全的。

discover:
  queries:
    - id: test_files
      type: glob
      patterns: ["**/*.test.{ts,tsx}", "**/*.spec.{ts,tsx}"]
    - id: source_files
      type: glob
      patterns: ["src/**/*.{ts,tsx}"]
  verbosity: files_only

关键规则:

  • 如果没有测试,请在重构之前编写测试
  • 测试可作为安全网,确保行为保持不变
  • 在重构前后都运行测试
阶段 2:提取函数/组件

大型函数和组件难以测试和维护。提取可复用的部分。

步骤 2.1:找出提取机会

寻找重复的逻辑,或只负责一项工作的代码块。

precision_read:
  files:
    - path: "src/components/UserProfile.tsx"
      extract: symbols
  verbosity: standard

适合提取的内容:

  • 只执行一项特定任务的代码块
  • 多处重复的逻辑
  • 超过 50 行的函数
  • 承担多项职责的 React 组件
步骤 2.2:提取函数

之前(重复的验证逻辑):

// In user-routes.ts
export async function POST(request: Request) {
  const body = await request.json();
  if (!body.email || !body.email.includes('@')) {
    return Response.json({ error: 'Invalid email' }, { status: 400 });
  }
  // ... rest of logic
}

// In profile-routes.ts
export async function PUT(request: Request) {
  const body = await request.json();
  if (!body.email || !body.email.includes('@')) {
    return Response.json({ error: 'Invalid email' }, { status: 400 });
  }
  // ... rest of logic
}

之后(提取验证逻辑):

// lib/validation.ts
import { z } from 'zod';

export const emailSchema = z.string().email();

export function validateEmail(email: unknown): { valid: true; email: string } | { valid: false; error: string } {
  const result = emailSchema.safeParse(email);
  if (!result.success) {
    return { valid: false, error: 'Invalid email format' };
  }
  return { valid: true, email: result.data };
}

// user-routes.ts
import { validateEmail } from '@/lib/validation';

export async function POST(request: Request) {
  const body = await request.json();
  const emailResult = validateEmail(body.email);
  if (!emailResult.valid) {
    return Response.json({ error: emailResult.error }, { status: 400 });
  }
  // ... rest of logic using emailResult.email
}

使用 precision_edit 执行提取:

precision_edit:
  operations:
    - action: replace
      path: "src/api/user-routes.ts"
      old_text: |
        const body = await request.json();
        if (!body.email || !body.email.includes('@')) {
          return Response.json({ error: 'Invalid email' }, { status: 400 });
        }
      new_text: |
        const body = await request.json();
        const emailResult = validateEmail(body.email);
        if (!emailResult.valid) {
          return Response.json({ error: emailResult.error }, { status: 400 });
        }
  verbosity: minimal
步骤 2.3:提取 React 组件

之前(承担多项职责的大型组件):

function UserProfile({ userId }: { userId: string }) {
  const [user, setUser] = useState<User | null>(null);
  const [posts, setPosts] = useState<Post[]>([]);
  const [followers, setFollowers] = useState<User[]>([]);

  useEffect(() => {
    fetch(`/api/users/${userId}`).then(r => r.json()).then(setUser);
    fetch(`/api/users/${userId}/posts`).then(r => r.json()).then(setPosts);
    fetch(`/api/users/${userId}/followers`).then(r => r.json()).then(setFollowers);
  }, [userId]);

  if (!user) return <div>Loading...</div>;

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.bio}</p>
      
      <div>
        <h2>Posts</h2>
        {posts.map(post => (
          <div key={post.id}>
            <h3>{post.title}</h3>
            <p>{post.content}</p>
          </div>
        ))}
      </div>
      
      <div>
        <h2>Followers</h2>
        {followers.map(follower => (
          <div key={follower.id}>
            <img src={follower.avatar} alt="" />
            <span>{follower.name}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

之后(提取组件和钩子):

// hooks/useUser.ts
export function useUser(userId: string) {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then(r => r.json())
      .then(data => {
        setUser(data);
        setLoading(false);
      });
  }, [userId]);

  return { user, loading };
}

// components/UserPosts.tsx
export function UserPosts({ userId }: { userId: string }) {
  const [posts, setPosts] = useState<Post[]>([]);

  useEffect(() => {
    fetch(`/api/users/${userId}/posts`).then(r => r.json()).then(setPosts);
  }, [userId]);

  return (
    <div>
      <h2>Posts</h2>
      {posts.map(post => (
        <PostCard key={post.id} post={post} />
      ))}
    </div>
  );
}

// components/UserFollowers.tsx
export function UserFollowers({ userId }: { userId: string }) {
  const [followers, setFollowers] = useState<User[]>([]);

  useEffect(() => {
    fetch(`/api/users/${userId}/followers`).then(r => r.json()).then(setFollowers);
  }, [userId]);

  return (
    <div>
      <h2>Followers</h2>
      {followers.map(follower => (
        <FollowerCard key={follower.id} user={follower} />
      ))}
    </div>
  );
}

// components/UserProfile.tsx
function UserProfile({ userId }: { userId: string }) {
  const { user, loading } = useUser(userId);

  if (loading || !user) return <div>Loading...</div>;

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.bio}</p>
      <UserPosts userId={userId} />
      <UserFollowers userId={userId} />
    </div>
  );
}

优点:

  • 每个组件只负责一项职责
  • 更容易单独测试
  • 可在整个应用中复用
  • 性能更佳(可以单独记忆化各部分)
阶段 3:重命名和重新组织

命名不佳、文件组织混乱会拖慢开发。

步骤 3.1:改进命名

使用描述性名称重命名变量、函数和文件。

不佳的命名:

function getData(id: string) { ... }  // Too generic
const x = getUserById(userId);  // Unclear abbreviation
let flag = true;  // What does this flag represent?

良好的命名:

function getUserProfile(userId: string) { ... }  // Specific, clear intent
const userProfile = getUserById(userId);  // Descriptive
let isEmailVerified = true;  // Boolean naming convention

使用 precision_edit 的 replace_all 进行重命名:

precision_edit:
  operations:
    - action: replace
      path: "src/lib/user.ts"
      old_text: "function getData"
      new_text: "function getUserProfile"
      replace_all: true
  verbosity: minimal
步骤 3.2:重新组织文件结构

使用基于功能或基于层次的结构,将相关文件归类在一起。

之前(扁平结构):

src/
  user-routes.ts
  user-service.ts
  user-repository.ts
  post-routes.ts
  post-service.ts
  post-repository.ts

之后(基于功能的结构):

src/
  features/
    users/
      api/
        routes.ts
      services/
        user-service.ts
      repositories/
        user-repository.ts
      types/
        user.types.ts
      index.ts  # Barrel export
    posts/
      api/
        routes.ts
      services/
        post-service.ts
      repositories/
        post-repository.ts
      types/
        post.types.ts
      index.ts

优点:

  • 相关代码集中在一起
  • 更容易查找文件
  • 功能边界清晰
  • Barrel 导出简化导入

使用 precision_write 创建 Barrel 导出:

precision_write:
  files:
    - path: "src/features/users/index.ts"
      content: |
        export * from './types/user.types';
        export * from './services/user-service';
        export * from './repositories/user-repository';
  verbosity: minimal
阶段 4:简化条件逻辑

嵌套条件和复杂的布尔逻辑容易出错。

步骤 4.1:使用守卫子句

之前(嵌套条件):

function processOrder(order: Order) {
  if (order.status === 'pending') {
    if (order.items.length > 0) {
      if (order.paymentConfirmed) {
        // Process order
        return processPayment(order);
      } else {
        throw new Error('Payment not confirmed');
      }
    } else {
      throw new Error('Order has no items');
    }
  } else {
    throw new Error('Order is not pending');
  }
}

之后(守卫子句):

function processOrder(order: Order) {
  if (order.status !== 'pending') {
    throw new Error('Order is not pending');
  }

  if (order.items.length === 0) {
    throw new Error('Order has no items');
  }

  if (!order.paymentConfirmed) {
    throw new Error('Payment not confirmed');
  }

  return processPayment(order);
}

优点:

  • 结构更扁平,更易阅读
  • 优先检查错误条件
  • 正常执行路径位于末尾
第 4.2 步:将条件逻辑提取为命名函数

之前(复杂的布尔逻辑):

if (user.role === 'admin' || (user.role === 'moderator' && user.permissions.includes('delete')) || user.id === post.authorId) {
  deletePost(post.id);
}

之后(命名函数):

function canDeletePost(user: User, post: Post): boolean {
  if (user.role === 'admin') return true;
  if (user.role === 'moderator' && user.permissions.includes('delete')) return true;
  if (user.id === post.authorId) return true;
  return false;
}

if (canDeletePost(user, post)) {
  deletePost(post.id);
}

优点:

  • 代码自解释
  • 逻辑可复用
  • 更易测试
第 4.3 步:对复杂条件逻辑使用策略模式

之前(冗长的 switch 语句):

function calculateShipping(order: Order): number {
  switch (order.shippingMethod) {
    case 'standard':
      return order.weight * 0.5;
    case 'express':
      return order.weight * 1.5 + 10;
    case 'overnight':
      return order.weight * 3 + 25;
    case 'international':
      return order.weight * 5 + 50;
    default:
      throw new Error('Unknown shipping method');
  }
}

之后(策略模式):

interface ShippingStrategy {
  calculate(weight: number): number;
}

class StandardShipping implements ShippingStrategy {
  calculate(weight: number): number {
    return weight * 0.5;
  }
}

class ExpressShipping implements ShippingStrategy {
  calculate(weight: number): number {
    return weight * 1.5 + 10;
  }
}

class OvernightShipping implements ShippingStrategy {
  calculate(weight: number): number {
    return weight * 3 + 25;
  }
}

class InternationalShipping implements ShippingStrategy {
  calculate(weight: number): number {
    return weight * 5 + 50;
  }
}

const shippingStrategies: Record<string, ShippingStrategy> = {
  standard: new StandardShipping(),
  express: new ExpressShipping(),
  overnight: new OvernightShipping(),
  international: new InternationalShipping(),
};

function calculateShipping(order: Order): number {
  const strategy = shippingStrategies[order.shippingMethod];
  if (!strategy) {
    throw new Error('Unknown shipping method');
  }
  return strategy.calculate(order.weight);
}

优点:

  • 开闭原则(易于添加新策略)
  • 每个策略都可以独立测试
  • 消除冗长的 switch 语句
第 5 阶段:改进类型

强类型可以在编译时捕获错误。

第 5.1 步:消除 any 类型

查找所有 any 用法:

precision_grep:
  queries:
    - id: any_usage
      pattern: ":\\s*any(\\s|;|,|\\))"
      glob: "**/*.{ts,tsx}"
  output:
    format: locations
  verbosity: standard

之前(不安全):

function processData(data: any) {
  return data.value.toUpperCase();  // Runtime error if value is not a string
}

之后(类型安全):

interface DataWithValue {
  value: string;
}

function processData(data: DataWithValue): string {
  return data.value.toUpperCase();
}
第 5.2 步:使用可辨识联合类型

之前(类型较弱):

interface ApiResponse {
  success: boolean;
  data?: User;
  error?: string;
}

function handleResponse(response: ApiResponse) {
  if (response.success) {
    console.log(response.data.name);  // TypeScript can't guarantee data exists
  }
}

之后(可辨识联合类型):

type ApiResponse =
  | { success: true; data: User }
  | { success: false; error: string };

function handleResponse(response: ApiResponse) {
  if (response.success) {
    console.log(response.data.name);  // TypeScript knows data exists here
  } else {
    console.error(response.error);  // TypeScript knows error exists here
  }
}

优点:

  • 全面类型检查
  • 无法表示不可能的状态
  • 更好的 IntelliSense
第 5.3 步:添加泛型约束

之前(泛化过度):

function getProperty<T>(obj: T, key: string) {
  return obj[key];  // Type error: no index signature
}

之后(恰当的约束):

function getProperty<T extends Record<string, unknown>, K extends keyof T>(
  obj: T,
  key: K
): T[K] {
  return obj[key];
}

const user = { name: 'Alice', age: 30 };
const name = getProperty(user, 'name');  // Type is string
const age = getProperty(user, 'age');    // Type is number

优点:

  • 类型推断可以正确工作
  • 在编译时捕获无效的键访问
第 6 阶段:依赖倒置

依赖抽象而非实现,以解耦代码。

第 6.1 步:提取接口

之前(紧耦合):

import { PrismaClient } from '@prisma/client';

class UserService {
  private prisma = new PrismaClient();

  async getUser(id: string) {
    return this.prisma.user.findUnique({ where: { id } });
  }
}

之后(依赖注入):

interface UserRepository {
  findById(id: string): Promise<User | null>;
  create(data: CreateUserInput): Promise<User>;
  update(id: string, data: UpdateUserInput): Promise<User>;
}

class PrismaUserRepository implements UserRepository {
  constructor(private prisma: PrismaClient) {}

  async findById(id: string): Promise<User | null> {
    return this.prisma.user.findUnique({ where: { id } });
  }

  async create(data: CreateUserInput): Promise<User> {
    return this.prisma.user.create({ data });
  }

  async update(id: string, data: UpdateUserInput): Promise<User> {
    return this.prisma.user.update({ where: { id }, data });
  }
}

class UserService {
  constructor(private userRepo: UserRepository) {}

  async getUser(id: string) {
    return this.userRepo.findById(id);
  }
}

// Usage
const prisma = new PrismaClient();
const userRepo = new PrismaUserRepository(prisma);
const userService = new UserService(userRepo);

优点:

  • 易于在测试中模拟(注入伪造的仓库)
  • 无需更改 UserService,即可将 Prisma 替换为其他 ORM
  • 关注点分离清晰
第 6.2 步:使用工厂模式

之前(难以测试):

function sendEmail(to: string, subject: string, body: string) {
  const client = new SendGridClient(process.env.SENDGRID_API_KEY!);  // BAD: Non-null assertion bypasses runtime validation
  client.send({ to, subject, body });
}

之后(工厂注入):

interface EmailClient {
  send(email: { to: string; subject: string; body: string }): Promise<void>;
}

class SendGridEmailClient implements EmailClient {
  constructor(private apiKey: string) {}

  async send(email: { to: string; subject: string; body: string }) {
    // SendGrid implementation
  }
}

class MockEmailClient implements EmailClient {
  async send(email: { to: string; subject: string; body: string }) {
    console.log('Mock email sent:', email);
  }
}

function createEmailClient(): EmailClient {
  if (process.env.NODE_ENV === 'test') {
    return new MockEmailClient();
  }
  const apiKey = process.env.SENDGRID_API_KEY;
  if (!apiKey) {
    throw new Error('SENDGRID_API_KEY environment variable is required');
  }
  return new SendGridEmailClient(apiKey);
}

function sendEmail(client: EmailClient, to: string, subject: string, body: string) {
  return client.send({ to, subject, body });
}

优点:

  • 测试不会发送真实邮件
  • 易于切换邮件服务提供商
  • 可按环境定制行为
第 7 阶段:数据库重构

数据库架构和查询需要谨慎重构。

第 7.1 步:添加索引以提升性能

检查现有架构:

precision_read:
  files:
    - path: "prisma/schema.prisma"
      extract: content
  verbosity: standard

之前(缺少索引):

model Post {
  id        String   @id @default(cuid())
  title     String
  published Boolean  @default(false)
  authorId  String
  createdAt DateTime @default(now())
}

之后(添加索引):

model Post {
  id        String   @id @default(cuid())
  title     String
  published Boolean  @default(false)
  authorId  String
  createdAt DateTime @default(now())

  @@index([authorId])
  @@index([published, createdAt])
}

适合添加索引的字段:

  • 外键(authorId、userId)
  • WHERE 子句中的字段(published、status)
  • ORDER BY 中的字段(createdAt、updatedAt)
  • 多列查询的复合索引
第 7.2 步:规范化数据库架构

之前(非规范化):

model Order {
  id            String @id
  customerName  String
  customerEmail String
  customerPhone String
}

之后(规范化):

model Customer {
  id     String @id
  name   String
  email  String @unique
  phone  String
  orders Order[]
}

model Order {
  id         String   @id
  customerId String
  customer   Customer @relation(fields: [customerId], references: [id])

  @@index([customerId])
}

优点:

  • 无数据重复
  • 在一个位置更新客户信息
  • 引用完整性
第 7.3 步:优化查询

之前(N+1 查询):

const posts = await prisma.post.findMany();
for (const post of posts) {
  const author = await prisma.user.findUnique({ where: { id: post.authorId } });
  console.log(`${post.title} by ${author.name}`);  // Note: Use structured logger in production
}

之后(预加载):

const posts = await prisma.post.findMany({
  include: {
    author: true,
  },
});
for (const post of posts) {
  console.log(`${post.title} by ${post.author.name}`);  // Note: Use structured logger in production
}

使用 discover 查找 N+1 模式:

discover:
  queries:
    - id: n_plus_one
      type: grep
      pattern: "(for|forEach|map).*await.*(prisma|db|query|find)"
      glob: "**/*.{ts,tsx,js,jsx}"
  verbosity: locations
第 8 阶段:验证和测试

只有经过测试验证,重构才是安全的。

第 8.1 步:重构前运行测试

建立基线:开始之前,测试必须通过。

precision_exec:
  commands:
    - cmd: "npm run test"
  verbosity: standard

如果测试失败,请先修复。

第 8.2 步:逐步重构

每次只做小幅更改,并在每一步之后进行验证。

工作流:

  1. 运行测试(建立基线)
  2. 进行一项重构更改
  3. 再次运行测试
  4. 如果测试通过,则提交并继续
  5. 如果测试失败,则回退并排查

使用 precision_exec 进行验证:

precision_exec:
  commands:
    - cmd: "npm run typecheck"
    - cmd: "npm run lint"
    - cmd: "npm run test"
  verbosity: standard
第 8.3 步:为重构后的代码添加测试

如果覆盖率下降,请添加测试。

precision_exec:
  commands:
    - cmd: "npm run test -- --coverage"
  verbosity: standard

检查覆盖率:

  • 函数覆盖率应达到 80% 以上
  • 关键路径覆盖率应达到 100%
  • 边界情况应有明确的测试
第 9 阶段:更新文档

重构后的代码需要更新文档。

第 9.1 步:更新 JSDoc 注释

之前(已过时):

/**
 * Gets user data from the database
 */
function getData(id: string) { ... }  // Function was renamed

之后(当前):

/**
 * Retrieves a user profile by ID including related posts and followers
 * @param userId - The unique identifier for the user
 * @returns User profile with posts and followers, or null if not found
 */
function getUserProfile(userId: string): Promise<UserProfile | null> { ... }
第 9.2 步:更新 README 和架构文档

如果文件结构发生变化,请更新文档。

更新示例:

  • 更新 README 中的文件树
  • 更新导入示例
  • 更新架构图
  • 更新贡献指南
阶段 10:自动化验证

运行验证脚本,确保重构质量。

./scripts/validate-refactoring.sh /path/to/project

脚本会验证:

  • 前后测试均通过
  • 代码重复减少
  • 类型安全性提高(没有新增 any 类型)
  • 函数长度在限制范围内
  • 消除导入循环
  • 测试覆盖率维持或提高

常见重构模式

请参阅 references/refactoring-patterns.md,了解详细的重构前后示例。

快速参考:

提取方法

适用场景: 函数过长或执行多项操作
解决方法: 将逻辑块提取到独立函数中

重命名变量/函数

适用场景: 名称不明确或具有误导性
解决方法: 将名称改为更具描述性,并遵循约定

用多态替换条件判断

适用场景: 根据类型执行的 switch 语句
解决方法: 使用策略模式或类层次结构

引入参数对象

适用场景: 函数参数过多
解决方法: 将相关参数组合成一个对象

替换魔法数字

适用场景: 使用了缺乏上下文的硬编码数字
解决方法: 将数字提取为具名常量

合并重复的条件代码片段

适用场景: 所有分支中都有相同的代码
解决方法: 将公共代码移到条件判断之外

用于重构的精准工具

Discover 工具

在整个代码库中查找适合重构的代码。

示例:查找重复代码

discover:
  queries:
    - id: validation_patterns
      type: grep
      pattern: "if.*!.*email.*includes"
      glob: "**/*.{ts,tsx}"
    - id: large_functions
      type: symbols
      query: "function"
  verbosity: locations
Precision Edit

执行安全、原子的重构编辑。

示例:提取函数

precision_edit:
  operations:
    - action: replace
      path: "src/api/user.ts"
      old_text: |
        if (!email || !email.includes('@')) {
          return { error: 'Invalid email' };
        }
      new_text: |
        const emailValidation = validateEmail(email);
        if (!emailValidation.valid) {
          return { error: emailValidation.error };
        }
  verbosity: minimal
Precision Exec

验证重构不会造成任何问题。

precision_exec:
  commands:
    - cmd: "npm run typecheck"
    - cmd: "npm run lint -- --fix"
    - cmd: "npm run test"
  verbosity: standard

验证脚本

使用 scripts/validate-refactoring.sh 验证重构质量。

./scripts/validate-refactoring.sh /path/to/project

脚本检查:

  • 更改前后测试均通过
  • 代码重复度指标
  • 类型安全性(没有新增 any 类型)
  • 函数长度限制
  • 导入循环
  • 测试覆盖率维持不变

快速参考

重构检查清单

重构前:

  • 测试已编写且通过
  • 了解当前代码结构
  • 确定重构目标(提取、重命名、重新组织)
  • 列出所有使用位置

重构期间:

  • 进行小幅、渐进式更改
  • 每次更改后运行测试
  • 让每次提交保持聚焦且原子化
  • 更新导入和引用

重构后:

  • 所有测试均通过
  • 类型检查通过
  • Lint 检查通过
  • 测试覆盖率维持或提高
  • 更新文档
  • 运行验证脚本
何时进行重构

适合重构的时机:

  • 添加新功能前(先清理代码)
  • 修复 Bug 时(修复的同时改进结构)
  • 代码审查期间(提出改进建议)
  • 发现代码异味时(重复、复杂)

不适合重构的时机:

  • 调试紧急问题时(先修复,之后再重构)
  • 没有测试时(不安全,可能破坏行为)
  • 临近截止日期时(可能引入 Bug)
  • 需求快速变化时
重构安全规则
  1. 测试是必需的 — 没有测试时绝不重构
  2. 一次只做一项更改 — 不要将重构与功能开发混在一起
  3. 频繁验证 — 每次更改后运行测试
  4. 原子化提交 — 每次提交都应处于完整且可正常工作的状态
  5. 保持行为不变 — 重构不应改变功能

高级技巧

使用精准工具自动重构

使用 discover 查找模式,然后使用 precision_edit 修复。

# Step 1: Find all console.log statements
discover:
  queries:
    - id: console_logs
      type: grep
      pattern: "console\\.log\\("
      glob: "src/**/*.{ts,tsx}"
  verbosity: locations

# Step 2: Replace with proper logger
precision_edit:
  operations:
    - action: replace
      path: "src/file1.ts"
      old_text: "console.log("
      new_text: "logger.info("
      replace_all: true
  verbosity: minimal
并行重构

使用批量操作同时重构多个文件。

precision_edit:
  operations:
    - action: replace
      path: "src/api/user.ts"
      old_text: "getData"
      new_text: "getUserProfile"
      replace_all: true
    - action: replace
      path: "src/api/post.ts"
      old_text: "getData"
      new_text: "getPostDetails"
      replace_all: true
  verbosity: minimal
重构指标

追踪改进情况。

重构前测量:

precision_exec:
  commands:
    - cmd: "find src -not -path '*/node_modules/*' -not -path '*/dist/*' -name '*.ts' -exec wc -l {} + | tail -1"
    - cmd: "grep -r --include='*.ts' --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=.git --exclude-dir=.next -- 'any' src | wc -l"
  verbosity: standard

重构后测量并比较:

  • 代码行数(移除重复代码后应减少)
  • any 的使用次数(应减少)
  • 文件数量(拆分大型文件后可能增加)
  • 测试覆盖率(应维持或提高)

与其他技能集成

  • 使用 code-review 识别重构需求
  • 使用 type-safety 技能了解类型改进模式
  • 重构错误处理逻辑时使用 error-handling 技能
  • 使用 testing-strategy 确保测试覆盖充分
  • 重构 UI 组件时使用 component-architecture
  • 重构数据库查询时使用 database-layer

资源

  • references/refactoring-patterns.md - 常见重构模式及示例
  • scripts/validate-refactoring.sh - 自动化重构验证
  • Martin Fowler 的重构目录 - https://refactoring.com/catalog/
  • Robert C. Martin 的《代码整洁之道》
  • Martin Fowler 的《重构:改善既有代码的设计》

评论

支持 Markdown