Files
monisuo/.agents/skills/clean-code/SKILL.md
2026-03-23 00:08:19 +08:00

11 KiB
Raw Blame History

name, description
name description
clean-code Clean Code 代码质量审查和重构指南。用于代码审查、重构、提升代码质量、减少技术债务。触发词代码审查、clean code、重构、代码质量、技术债务、代码规范。

Clean Code 技能

帮助编写高质量、可维护、可读性强的代码。

核心原则

1. 有意义的命名

// ❌ 坏
const d = new Date();
const ymd = date.split('-');

// ✅ 好
const currentDate = new Date();
const [year, month, day] = date.split('-');

规则:

  • 使用描述性名称,避免缩写
  • 名称应该表达意图
  • 避免误导性名称
  • 做有意义的区分(不是 a1, a2
  • 使用可搜索的名称
  • 类名用名词,方法名用动词

2. 函数

// ❌ 坏 - 做太多事
function processUser(user: User) {
  // 验证
  if (!user.email) throw new Error('Email required');
  if (!user.name) throw new Error('Name required');

  // 保存
  database.save(user);

  // 发送邮件
  emailService.send(user.email, 'Welcome!');

  // 记录日志
  logger.log(`User ${user.name} created`);
}

// ✅ 好 - 单一职责
function validateUser(user: User): void {
  if (!user.email) throw new Error('Email required');
  if (!user.name) throw new Error('Name required');
}

function saveUser(user: User): void {
  database.save(user);
}

function sendWelcomeEmail(user: User): void {
  emailService.send(user.email, 'Welcome!');
}

function logUserCreation(user: User): void {
  logger.log(`User ${user.name} created`);
}

function processUser(user: User) {
  validateUser(user);
  saveUser(user);
  sendWelcomeEmail(user);
  logUserCreation(user);
}

规则:

  • 函数应该小(<20行
  • 只做一件事
  • 每个函数一个抽象层级
  • 使用描述性名称
  • 函数参数越少越好理想0-3个
  • 避免副作用
  • 分隔指令与询问

3. 注释

// ❌ 坏 - 多余的注释
// 检查用户是否已激活
if (user.isActive) { ... }

// ✅ 好 - 代码自解释
if (user.isActive) { ... }

// ✅ 好 - 解释为什么
// 使用 setTimeout 而不是 setInterval 避免重叠执行
setTimeout(processQueue, 1000);

规则:

  • 好的代码是自解释的
  • 注释不能弥补糟糕的代码
  • 用代码表达意图
  • 好的注释法律信息、解释意图、警示、TODO
  • 坏的注释:喃喃自语、多余的、误导的、注释掉的代码

4. 格式

// ❌ 坏
export class User{constructor(private name:string,private age:number){}
getName(){return this.name;}}

// ✅ 好
export class User {
  constructor(
    private name: string,
    private age: number
  ) {}

  getName(): string {
    return this.name;
  }
}

规则:

  • 垂直格式:概念之间用空行分隔
  • 水平格式:行宽<120字符
  • 缩进统一使用2或4空格
  • 团队规则:遵循项目既定风格

5. 对象和数据结构

// ❌ 坏 - 暴露内部结构
class User {
  public name: string;
  public age: number;
}

// ✅ 好 - 隐藏实现
class User {
  private _name: string;
  private _age: number;

  get name(): string {
    return this._name;
  }

  set age(value: number) {
    if (value < 0) throw new Error('Invalid age');
    this._age = value;
  }
}

规则:

  • 数据抽象:隐藏实现
  • 数据、对象的反对称性
  • 得墨忒耳定律:模块不应知道它操作对象的内部细节

6. 错误处理

// ❌ 坏 - 返回 null
function getUser(id: string): User | null {
  return database.find(id);
}

// ✅ 好 - 抛出异常
function getUser(id: string): User {
  const user = database.find(id);
  if (!user) throw new UserNotFoundError(id);
  return user;
}

// ✅ 好 - 使用 Special Case 模式
class NullUser implements User {
  name = 'Guest';
  age = 0;
}

规则:

  • 使用异常而非返回码
  • 先写 Try-Catch-Finally
  • 给出异常的上下文
  • 别返回 null 值
  • 别传递 null 值

7. 边界

// ❌ 坏 - 直接依赖第三方类
import { Map } from 'third-party-lib';

class UserCollection {
  private users: Map<string, User>;
}

// ✅ 好 - 使用适配器模式
interface UserMap {
  get(key: string): User;
  set(key: string, user: User): void;
}

class ThirdPartyUserMap implements UserMap {
  private map: Map<string, User>;

  get(key: string): User {
    return this.map.get(key);
  }

  set(key: string, user: User): void {
    this.map.set(key, user);
  }
}

规则:

  • 隐藏第三方代码
  • 使用适配器模式
  • 边界处的代码需要清晰的分割和测试

8. 单元测试

// ❌ 坏 - 不清晰的测试
test('user', () => {
  const u = new User('John', 25);
  expect(u.getName()).toBe('John');
  expect(u.getAge()).toBe(25);
});

// ✅ 好 - BUILD-OPERATE-CHECK 模式
test('shouldReturnUserName', () => {
  // BUILD
  const user = new User('John', 25);

  // OPERATE
  const name = user.getName();

  // CHECK
  expect(name).toBe('John');
});

// ✅ 好 - Given-When-Then 模式
test('shouldReturnUserName', () => {
  // Given
  const user = new User('John', 25);

  // When
  const name = user.getName();

  // Then
  expect(name).toBe('John');
});

规则:

  • F.I.R.S.T 原则:
    • Fast快速
    • Independent独立
    • Repeatable可重复
    • Self-Validating自验证
    • Timely及时
  • 每个测试一个断言
  • 单一概念
  • 测试代码和生产代码一样重要

9. 类

// ❌ 坏 - 大类
class UserManager {
  createUser() {}
  deleteUser() {}
  updateUser() {}
  sendEmail() {}
  validateEmail() {}
  generateReport() {}
}

// ✅ 好 - 单一职责
class UserService {
  create(user: User) {}
  delete(id: string) {}
  update(user: User) {}
}

class EmailService {
  send(email: string, content: string) {}
  validate(email: string): boolean {}
}

class ReportService {
  generate(userId: string): Report {}
}

规则:

  • 类应该小
  • 单一职责原则SRP
  • 内聚性:方法和数据互相依赖
  • 保持内聚性就会得到许多短小的类

10. 系统

// ❌ 坏 - 硬编码依赖
class UserService {
  private db = new Database(); // 硬编码
}

// ✅ 好 - 依赖注入
class UserService {
  constructor(private db: Database) {}
}

// ✅ 好 - 工厂模式
class ServiceFactory {
  static createUserService(): UserService {
    return new UserService(new Database());
  }
}

规则:

  • 分离构造和使用
  • 依赖注入
  • 扩充AOP面向切面编程
  • 测试驱动系统架构

代码审查清单

命名

  • 变量名是否描述了其用途?
  • 函数名是否描述了其行为?
  • 类名是否描述了其职责?
  • 名称是否一致?

函数

  • 函数是否小于20行
  • 函数是否只做一件事?
  • 函数参数是否<=3个
  • 函数是否有副作用?
  • 函数名是否描述性?

结构

  • 代码是否有清晰的层次结构?
  • 类是否遵循单一职责原则?
  • 是否有重复代码?
  • 依赖是否清晰?

测试

  • 是否有足够的测试覆盖?
  • 测试是否快速?
  • 测试是否独立?
  • 测试是否清晰?

错误处理

  • 是否处理了所有可能的错误?
  • 错误信息是否清晰?
  • 是否避免了返回 null

重构技巧

提取方法

// Before
function printOwing(invoice: Invoice) {
  let outstanding = 0;

  // 打印横幅
  console.log('***********************');
  console.log('*** Customer Owes ***');
  console.log('***********************');

  // 计算未付款
  for (const order of invoice.orders) {
    outstanding += order.amount;
  }

  // 打印详情
  console.log(`name: ${invoice.customer}`);
  console.log(`amount: ${outstanding}`);
}

// After
function printOwing(invoice: Invoice) {
  printBanner();
  const outstanding = calculateOutstanding(invoice);
  printDetails(invoice, outstanding);
}

function printBanner() {
  console.log('***********************');
  console.log('*** Customer Owes ***');
  console.log('***********************');
}

function calculateOutstanding(invoice: Invoice): number {
  return invoice.orders.reduce((sum, order) => sum + order.amount, 0);
}

function printDetails(invoice: Invoice, outstanding: number) {
  console.log(`name: ${invoice.customer}`);
  console.log(`amount: ${outstanding}`);
}

内联方法

// Before
function getRating(driver: Driver): number {
  return moreThanFiveLateDeliveries(driver) ? 2 : 1;
}

function moreThanFiveLateDeliveries(driver: Driver): boolean {
  return driver.numberOfLateDeliveries > 5;
}

// After
function getRating(driver: Driver): number {
  return driver.numberOfLateDeliveries > 5 ? 2 : 1;
}

提取变量

// Before
if (platform.toUpperCase().indexOf('MAC') > -1 &&
    browser.toUpperCase().indexOf('IE') > -1 &&
    wasInitialized() && resize > 0) {
  // do something
}

// After
const isMacOs = platform.toUpperCase().indexOf('MAC') > -1;
const isIE = browser.toUpperCase().indexOf('IE') > -1;
const wasResized = resize > 0;

if (isMacOs && isIE && wasInitialized() && wasResized) {
  // do something
}

分解条件

// Before
if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
  charge = quantity * winterRate + winterServiceCharge;
} else {
  charge = quantity * summerRate;
}

// After
if (isSummer(date)) {
  charge = summerCharge(quantity);
} else {
  charge = winterCharge(quantity);
}

function isSummer(date: Date): boolean {
  return !date.before(SUMMER_START) && !date.after(SUMMER_END);
}

function summerCharge(quantity: number): number {
  return quantity * summerRate;
}

function winterCharge(quantity: number): number {
  return quantity * winterRate + winterServiceCharge;
}

以多态取代条件

// Before
function calculatePay(employee: Employee): number {
  switch (employee.type) {
    case 'ENGINEER':
      return employee.monthlySalary;
    case 'SALESMAN':
      return employee.monthlySalary + employee.commission;
    case 'MANAGER':
      return employee.monthlySalary + employee.bonus;
    default:
      throw new Error('Invalid employee type');
  }
}

// After
abstract class Employee {
  abstract calculatePay(): number;
}

class Engineer extends Employee {
  calculatePay(): number {
    return this.monthlySalary;
  }
}

class Salesman extends Employee {
  calculatePay(): number {
    return this.monthlySalary + this.commission;
  }
}

class Manager extends Employee {
  calculatePay(): number {
    return this.monthlySalary + this.bonus;
  }
}

使用方法

  1. 代码审查时:参考清单逐项检查
  2. 重构时:应用重构技巧
  3. 新功能开发时:遵循核心原则
  4. 代码坏味道识别:参考常见问题

告诉我需要审查的代码,我会帮你识别问题并提供改进建议!