关于 clean-code
用于分析、重构和维护软件的 clean-code 技能。它引导开发者评估代码异味,并运用 SOLID 原则、命名规范、错误处理和复杂度管理。
主要功能
- 01分析代码上下文与复杂度
- 02识别并确定代码异味优先级
- 03逐步执行重构
- 04运用 SOLID 和 DRY 原则
- 05指导命名与错误处理
适用场景
- 01重构前评估代码库
- 02规划减少代码异味的修改
- 03提升可读性和可测试性
- 04重构时保持程序行为
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ pixel-process-ug/ superkit-agents --skill clean-code安装后在 Claude Code 中输入/clean-code确认已加载。
详细介绍
清洁代码
概述
运用清洁代码原则,打造可读、可维护、可测试的软件。本技能涵盖 SOLID 原则、DRY 实践、代码异味识别、重构模式、命名规范、错误处理和复杂度管理。基于 Robert C. Martin、Martin Fowler 和 Kent Beck 的著作。
开始时宣布:“我正在使用 clean-code 技能来提升代码质量。”
阶段 1:分析当前代码
目标: 在修改任何内容之前,结合完整上下文阅读并理解代码。
操作
- 在完整上下文中阅读代码(而不只是代码片段)
- 明确代码的职责和用途
- 衡量圈复杂度
- 梳理耦合关系和依赖项
- 记录现有测试
停止——在进入阶段 2 之前,必须满足:
- 已在完整上下文中阅读代码
- 已理解代码的用途和职责
- 已识别复杂度热点
- 已了解现有测试覆盖情况
阶段 2:识别代码异味
目标: 使用下方参考表,列出所有代码异味。
膨胀症
| 异味 | 检测方式 | 重构方法 |
|---|---|---|
| 过长方法 | > 30 行 | 提取方法 |
| 过大类 | > 300 行或 > 5 项职责 | 提取类 |
| 参数列表过长 | > 3 个参数 | 引入参数对象 |
| 数据泥团 | 相同参数总是一起出现 | 提取类 |
| 基本类型偏执 | 使用基本类型,而不是小型对象 | 替换为值对象 |
面向对象滥用者
| 异味 | 检测方式 | 重构方法 |
|---|---|---|
| Switch 语句 | 根据类型进行 Switch 判断 | 替换为多态 |
| 平行继承体系 | 每个子类都需要对应的平行子类 | 合并层次结构 |
| 被拒绝的遗赠 | 子类忽略继承的方法 | 用委托替换继承 |
变更阻碍因素
| 异味 | 检测方式 | 重构方法 |
|---|---|---|
| 发散式变化 | 一个类因多种原因而修改 | 提取类(SRP) |
| 散弹式修改 | 一项更改影响多个类 | 移动方法、内联类 |
可有可无项
| 异味 | 检测方式 | 重构方法 |
|---|---|---|
| 死代码 | 无法到达或未使用 | 删除 |
| 投机性通用性 | “以防万一”而创建的未使用抽象 | 收起层次结构、删除 |
| 解释糟糕代码的注释 | 注释在弥补代码表达不清的问题 | 重命名、提取方法 |
停止——在进入阶段 3 之前,必须满足:
- 已列出所有代码异味
- 已为每个异味确定优先级(高/中/低)
- 已为每个异味确定重构方法
阶段 3:执行重构
目标: 一次应用一种重构模式,并在每次之后验证测试。
操作
- 每次只应用一种重构
- 每次更改后运行测试
- 如果任何测试失败,立即回滚
- 持续进行,直到代码整洁
- 检查命名、结构和文档
停止——满足以下条件时,重构完成:
- 所有高优先级异味都已解决
- 每次更改后,所有测试均通过
- 重构期间未更改任何行为
- 新团队成员能够读懂代码
SOLID 原则
S — 单一职责原则
一个类/模块应该有且只有一个变更理由。
异味: 一个类因多种无关原因而修改。
修复: 将不同职责提取到独立的类中。
O — 开闭原则
对扩展开放,对修改关闭。
异味: Switch 语句随着新类型的增加而不断增长。
修复: 使用多态、策略模式或插件架构。
L — 里氏替换原则
子类型必须能够替换其基类型。
异味: 子类重写方法并抛出“not supported”。
修复: 重构层次结构;优先使用组合而非继承。
I — 接口隔离原则
客户端不应依赖它不使用的方法。
异味: 接口包含许多方法;实现者会将一些方法留空。 修复: 将接口拆分为更小、更专注的接口。
D — 依赖倒置原则
依赖抽象,而非具体实现。
异味: 高层模块直接导入低层模块。 修复: 通过接口/抽象类注入依赖。
命名约定
规则
| 元素 | 约定 | 示例 |
|---|---|---|
| 变量 | 用描述其所保存内容的名词命名 | userCount,而不是 n |
| 布尔值 | 以 is/has/can/should 为前缀 | isActive、hasPermission |
| 函数 | 用描述其作用的动词命名 | calculateTotal、fetchUsers |
| 常量 | UPPER_SNAKE_CASE | MAX_RETRY_COUNT |
| 类 | 使用 PascalCase 名词 | UserRepository、PaymentService |
| 接口 | 描述能力 | Serializable、Cacheable |
名称长度指南
| 作用域 | 长度 | 示例 |
|---|---|---|
| 循环计数器 | 1-2 个字符 | i、j(仅限极小循环) |
| Lambda 参数 | 上下文明确时使用 1-3 个字符 | users.filter(u => u.active) |
| 局部变量 | 简短但具有描述性 | total、result |
| 函数名称 | 中等长度,具有描述性 | calculateMonthlyRevenue |
| 类名称 | 按需命名 | AuthenticationTokenValidator |
函数指南
大小和结构
- 函数应只做一件事
- 理想长度:5-15 行(不包括样板代码)
- 最长:30 行(超过此长度时应拆分)
- 参数最多:3 个(超过此数量时使用选项对象)
守卫子句(提前返回)
// Bad: nested conditions
function getDiscount(user) {
if (user) {
if (user.isPremium) {
if (user.orderCount > 10) {
return 0.2;
}
}
}
return 0;
}
// Good: guard clauses
function getDiscount(user) {
if (!user) return 0;
if (!user.isPremium) return 0;
if (user.orderCount <= 10) return 0;
return 0.2;
}
错误处理模式
决策表
| 方法 | 适用场景 | 示例 |
|---|---|---|
| Result 类型 | 函数式风格、预期内的失败 | Result<T, E> 返回类型 |
| 特定异常 | 面向对象风格、异常情况 | throw new ValidationError(...) |
| 错误代码 | C 风格 API、跨语言场景 | 返回代码 + 消息 |
| Option/Maybe | 值可能存在,也可能不存在 | Option<User> |
Result 类型模式
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
function parseConfig(raw: string): Result<Config, ParseError> {
try {
const config = JSON.parse(raw);
if (!isValidConfig(config)) {
return { success: false, error: new ParseError('Invalid config structure') };
}
return { success: true, data: config };
} catch {
return { success: false, error: new ParseError('Invalid JSON') };
}
}
错误处理禁忌清单
- 切勿捕获并悄无声息地忽略错误
- 切勿将异常用于控制流
- 切勿用 null 表示错误
- 切勿在记录错误后重新抛出,却不补充上下文
复杂度指标
| 范围 | 风险级别 | 操作 |
|---|---|---|
| 1-5 | 低 | 无需操作 |
| 6-10 | 中等 | 考虑重构 |
| 11-20 | 高 | 应进行重构 |
| 21+ | 严重 | 必须重构 |
降低复杂度
- 将复杂条件提取为命名布尔值
- 使用守卫子句替代嵌套条件语句
- 使用多态替代类型检查
- 拆分为更小的函数
- 使用查找表替代 switch/if 链
DRY 应用决策表
| 情况 | 是否应用 DRY? | 理由 |
|---|---|---|
| 逻辑完全重复 | 是 | 相同逻辑应只在一处实现 |
| 出现三次或更多 | 是 | 三次原则验证了这一模式 |
| 仅出现两次 | 等一等 | 可能只是偶然相似 |
| 结构相似,但用途不同 | 否 | 变更原因不同 |
| 抽象会增加更多复杂度 | 否 | 清晰度优先于 DRY |
注释理念
好的注释
| 类型 | 示例 |
|---|---|
| 原因(推理过程) | // 使用二分查找,因为列表已排序且项目数超过 10K |
| 法律声明 | 版权、许可证头部 |
| 附带工单的 TODO | // TODO(PROJ-123): 添加速率限制 |
| 警告 | // WARNING: 这不是线程安全的 |
| 公共 API 文档 | 公共接口的 JSDoc/TSDoc |
不好的注释(删除注释,改为修正代码)
| 类型 | 示例 |
|---|---|
| 复述代码 | 在 counter++ 前写 // 增加计数器 |
| 已注释掉的代码 | 改用版本控制 |
| 日志式注释 | 改用 git log |
| 闭合大括号注释 | } // end if |
反模式/常见错误
| 反模式 | 错误原因 | 正确做法 |
|---|---|---|
| 过早抽象 | 将意图不同的代码强行 DRY 化 | 等到符合三次原则时再处理 |
| 上帝类 | 什么都知道,什么都做 | 按职责拆分(SRP) |
| 特性嫉妒 | 方法使用其他类的数据多于自身数据 | 将方法移至数据所属的类 |
| 字符串类型数据 | 应使用枚举/类型的地方却使用字符串 | 定义合适的类型 |
| 魔法数字 | 含义不明确,容易出错 | 使用具名常量 |
| 布尔陷阱 | 函数使用会改变行为的布尔参数 | 使用具名选项或拆分为多个函数 |
| 过度设计 | 为不存在的问题创建抽象 | YAGNI——你不会需要它 |
集成点
| 技能 | 关系 |
|---|---|
code-review | 代码审查会识别代码异味,供 clean-code 处理 |
test-driven-development | TDD 可确保重构过程中保留行为 |
senior-frontend | 前端组件遵循整洁代码原则 |
senior-backend | 后端服务遵循 SOLID 和整洁架构 |
performance-optimization | 整洁代码有助于更轻松地进行性能优化 |
systematic-debugging | 整洁代码更易于调试 |
不可变性偏好
- 默认使用
const(JavaScript/TypeScript) - 使用
readonly属性和ReadonlyArray - 优先使用展开/解构,而不是变更数据
- 对状态使用不可变更新模式
- 只有性能分析表明有必要时才进行变更
技能类型
灵活——根据上下文应用原则。并非每个函数都需要只有 5 行;也并非每种模式都需要符合 SOLID。运用判断,以团队易读性为先,而非追求理论上的纯粹。
评论
支持 Markdown