claude-howto 重构技能参考:代码坏味道(Code Smells)完整识别与治理指南
【免费下载链接】claude-howtoA visual, example-driven guide to Claude Code — from basic concepts to advanced agents, with copy-paste templates that bring immediate value.项目地址: https://gitcode.com/GitHub_Trending/cl/claude-howto
本指南基于 claude-howto 仓库中 refactor 技能 的核心参考文档 code-smells.md,系统整理马丁·福勒《重构》第二版中的 22 种代码坏味道:坏味道是更深层问题的表面症状,它们本身不是 Bug,而是"设计可能出了问题"的信号。读完本文,你将掌握五大类坏味道的识别特征、危害、对应重构手法、量化阈值,以及如何借助仓库中的 detect-smells.py 与 analyze-complexity.py 脚本自动化检测与度量改进效果。
「代码坏味道是通常对应系统中更深层次问题的表面迹象。」—— 马丁·福勒
一、坏味道到底是什么
坏味道(Code Smell)是一种启发式信号:某段代码"闻起来不对劲",需要进一步调查。它与明确的错误不同——代码可能完全能运行,但其结构暗示了可维护性、可测试性或扩展性隐患。识别坏味道是 refactor 技能 的第三阶段核心工作,识别之后需要:
- 依据 refactoring-catalog.md 选择对应重构手法;
- 借助 templates/refactoring-plan.md 制定分阶段计划;
- 以"行为不变、小步快跑、测试护航"为原则实施。
福勒把坏味道分为五大家族,每一族代表一类结构问题:
| 家族 | 关注点 | 代表坏味道 |
|---|---|---|
| 臃肿体(Bloaters) | 代码膨胀到难以高效工作 | Long Method、Large Class、Primitive Obsession、Long Parameter List、Data Clumps |
| 滥用面向对象(OO Abusers) | OOP 原则被不完整或不正确使用 | Switch Statements、Temporary Field、Refused Bequest、Alternative Classes with Different Interfaces |
| 阻碍变更(Change Preventers) | 改一处必须连带改多处 | Divergent Change、Shotgun Surgery、Parallel Inheritance Hierarchies |
| 多余之物(Dispensables) | 没有存在价值、应当删除的东西 | Comments、Duplicate Code、Lazy Class、Dead Code、Speculative Generality |
| 耦合器(Couplers) | 类之间过度耦合 | Feature Envy、Inappropriate Intimacy、Message Chains、Middle Man |
二、臃肿体(Bloaters):代码过度膨胀的信号
臃肿体的共同特征是"某个元素长得太大,无法高效工作"。仓库的检测脚本 detect-smells.py 对这类坏味道给出了可量化的阈值定义(见THRESHOLDS字典,源码第 101–110 行),你可以直接作为硬性指标。
2.1 过长方法(Long Method)
识别特征:
- 方法超过30–50 行(detect-smells.py 中
long_method_lines: 30、very_long_method_lines: 50两个阈值分别对应中、高严重度); - 需要滚动才能看完整个方法;
- 存在多层嵌套;
- 用注释来解释每个代码段"在做什么"。
为什么有害:难以理解、难以隔离测试、修改的副作用难以预料,且内部往往藏着重复逻辑。源码中_check_method_length()(第 198–218 行)会在超过 50 行时报 HIGH 严重度、超过 30 行报 MEDIUM,建议直接应用 Extract Method 拆分。
重构手法:Extract Method、Replace Temp with Query、Introduce Parameter Object、Replace Method with Method Object、Decompose Conditional。
重构前:
function processOrder(order) { // 订单校验(20 行) if (!order.items) throw new Error('No items'); if (order.items.length === 0) throw new Error('Empty order'); // ... 更多校验 // 汇总计算(30 行) let subtotal = 0; for (const item of order.items) { subtotal += item.price * item.quantity; } // ... 税费、运费、折扣 // 发送通知(20 行) // ... email 逻辑 }重构后:
function processOrder(order) { validateOrder(order); const totals = calculateOrderTotals(order); sendOrderNotifications(order, totals); return { order, totals }; }2.2 过大类(Large Class)
识别特征:
- 实例字段超过7–10 个;
- 方法超过15–20 个;
- 类名含糊(Manager、Handler、Processor);
- 部分方法根本不用到某些实例字段。
检测阈值(来自 detect-smells.py 的_check_class_size(),第 292–318 行):
行数 > 300 方法数 > 10 字段数 > 10其中类行数超过 300 直接判定为 HIGH 严重度。这与文档中"代码行数 > 300、方法数 > 15、字段数 > 10"的速查标准相互印证。
为什么有害:违反单一职责原则、难以测试、变更波及无关功能、部分难以复用。
重构手法:Extract Class、Extract Subclass、Extract Interface。
2.3 基本类型偏执(Primitive Obsession)
识别特征:
- 用基本类型表达领域概念(用
string表示 email、用int表示金额); - 用基本类型数组代替对象;
- 用字符串常量表示类型码;
- 魔法数字 / 魔法字符串。
为什么有害:类型层面没有校验、业务逻辑散落在整个代码库、容易传入错误的值、领域概念缺失。detect-smells.py 中_detect_magic_numbers()(第 338–367 行)会把出现在运算或比较中、且不在白名单(0, 1, -1, 2, 100, true, false...)里的数值字面量标记为 LOW 严重度坏味道,建议替换为具名常量。
重构手法:Replace Primitive with Object、Replace Type Code with Class、Replace Type Code with Subclasses、Replace Type Code with State/Strategy。
重构前:
const user = { email: 'john@example.com', // 只是字符串 phone: '1234567890', // 只是字符串 status: 'active', // 魔法字符串 balance: 10050 // 用整数表示分 };重构后:
const user = { email: new Email('john@example.com'), phone: new PhoneNumber('1234567890'), status: UserStatus.ACTIVE, balance: Money.cents(10050) };2.4 过长参数列表(Long Parameter List)
识别特征:
- 方法参数超过4 个(detect-smells.py 中
max_parameters: 4,_detect_long_parameter_lists()第 220–256 行会在参数超过 6 个时报 HIGH、4–6 个报 MEDIUM,且自动过滤 Python 的self/cls); - 某些参数总是成对出现;
- 布尔标志位改变方法行为;
- 频繁传递 null/undefined。
为什么有害:难以正确调用、参数顺序易混淆、说明方法职责过多、新增参数困难。
重构手法:Introduce Parameter Object、Preserve Whole Object、Replace Parameter with Method Call、Remove Flag Argument。
重构前:
function createUser(firstName, lastName, email, phone, street, city, state, zip, isAdmin, isActive, createdBy) { // ... }重构后:
function createUser(personalInfo, address, options) { // personalInfo: { firstName, lastName, email, phone } // address: { street, city, state, zip } // options: { isAdmin, isActive, createdBy } }2.5 数据泥团(Data Clumps)
识别特征:
- 相同的 3 个以上字段反复一起出现;
- 参数总是结伴旅行;
- 类中有一部分字段天然属于彼此。
为什么有害:处理逻辑重复、缺少抽象、难以扩展,并且提示存在隐藏的类。
重构手法:Extract Class、Introduce Parameter Object、Preserve Whole Object。
示例:
// 数据泥团:坐标 (x, y, z) function movePoint(x, y, z, dx, dy, dz) { } function scalePoint(x, y, z, factor) { } function distanceBetween(x1, y1, z1, x2, y2, z2) { } // 抽出 Point3D 类 class Point3D { constructor(x, y, z) { } move(delta) { } scale(factor) { } distanceTo(other) { } }三、滥用面向对象(OO Abusers):OOP 原则被扭曲
3.1 Switch 语句(Switch Statements)
识别特征:
- 冗长的 switch/case 或 if/else 链;
- 同一 switch 出现在多个地方;
- 针对类型码做 switch;
- 新增 case 必须到处修改。
为什么有害:违反开闭原则、修改波及所有 switch 出现处、难以扩展、往往暗示缺失多态。detect-smells.py 的_detect_switch_statements()(第 429–469 行)对 Python 检测连续 4 个以上的if/elif ==链、对 JS/TS 检测 4 个以上case的 switch,统一给出 MEDIUM 严重度并建议 Replace Conditional with Polymorphism。
重构手法:Replace Conditional with Polymorphism、Replace Type Code with Subclasses、Replace Type Code with State/Strategy。
重构前:
function calculatePay(employee) { switch (employee.type) { case 'hourly': return employee.hours * employee.rate; case 'salaried': return employee.salary / 12; case 'commissioned': return employee.sales * employee.commission; } }重构后:
class HourlyEmployee { calculatePay() { return this.hours * this.rate; } } class SalariedEmployee { calculatePay() { return this.salary / 12; } }3.2 临时字段(Temporary Field)
识别特征:实例字段只在部分方法中使用;字段按条件设置;为特定场景做复杂的初始化。
为什么有害:字段存在却可能是 null、对象状态难以理解、暗示隐藏的条件逻辑。
重构手法:Extract Class、Introduce Null Object、Replace Temp Field with Local。
3.3 被拒绝的遗赠(Refused Bequest)
识别特征:子类不使用继承来的方法/数据;子类重写父类方法使其空转;用继承做代码复用而非表达 IS-A 关系。
为什么有害:抽象错误、违反里氏替换原则、层级关系具有误导性。
重构手法:Push Down Method/Field、Replace Subclass with Delegate、Replace Inheritance with Delegation。
3.4 异曲同工的类(Alternative Classes with Different Interfaces)
识别特征:两个类做着相似的事;对同一概念使用不同方法名;本可互换使用。
为什么有害:重复实现、缺少统一接口、切换困难。
重构手法:Rename Method、Move Method、Extract Superclass、Extract Interface。
四、阻碍变更(Change Preventers):改一处要牵连一片
这类坏味道的核心危害是"改一个东西需要连带改许多其他东西",变更成本高、风险大。
4.1 发散式变更(Divergent Change)
识别特征:一个类因为多种不同原因被修改;不同领域的变更都会触发对同一类的编辑;类是"上帝类"。
为什么有害:违反单一职责、变更频率高、合并冲突多。
重构手法:Extract Class、Extract Superclass、Extract Subclass。
示例:User类因为认证变更、资料变更、计费变更、通知变更而被反复修改 → 应抽出AuthService、ProfileService、BillingService、NotificationService。
4.2 霰弹式修改(Shotgun Surgery)
识别特征:一次变更要求编辑多个类;小功能需要改动 10+ 个文件;修改分散、难以找全。
为什么有害:容易遗漏、耦合度高、易出错。检测提示:如果给某处"增加一个字段"需要改动 5 个以上文件,就应当警惕。
重构手法:Move Method、Move Field、Inline Class。
4.3 平行继承体系(Parallel Inheritance Hierarchies)
识别特征:在一个层级中新建子类,就必须在另一个层级中新建对应子类;类名前缀互相呼应(如DatabaseOrder、DatabaseProduct)。
为什么有害:双重维护、层级间强耦合、容易漏改其中一边。
重构手法:Move Method、Move Field、消除其中一个层级。
五、多余之物(Dispensables):没有存在必要的东西
5.1 过多注释(Comments)
识别特征:注释解释"代码做了什么";被注释掉的代码;永远留着的 TODO/FIXME;注释里写道歉的话。
为什么有害:注释会撒谎(与代码脱节)、代码应当自文档化、死代码造成困惑。
重构手法:Extract Method(用方法名表达意图)、Rename、删除被注释代码、Introduce Assertion。
好注释 vs 坏注释:
// 坏:解释"做了什么" // 遍历用户并检查是否活跃 for (const user of users) { if (user.status === 'active') { } } // 好:解释"为什么" // 只保留活跃用户——非活跃用户由清理任务处理 const activeUsers = users.filter(u => u.isActive);detect-smells.py 的_detect_excessive_comments()(第 369–389 行)会用正则匹配set/get/return/loop/iterate/check/if/increment/decrement等"解释做什么"的注释动词,将其标记为 LOW 严重度坏味道。
5.2 重复代码(Duplicate Code)
识别特征:同一代码出现在多处;相似代码只有细微差异;复制粘贴模式。
为什么有害:修 Bug 要修多处、不一致风险、代码库膨胀。检测规则:任何重复 3 次以上的代码都应被抽出。detect-smells.py 的_detect_duplicate_code()(第 491–513 行)对规范化后长度超过 20 字符、出现至少 3 次的行进行标记。
重构手法:Extract Method、Extract Class、Pull Up Method(层级内)、Form Template Method。
5.3 懒散类(Lazy Class)
识别特征:类做的事情不足以证明其存在;没有附加价值的包装层;过度设计的产物。
为什么有害:维护开销、不必要的间接层、无益的复杂度。
重构手法:Inline Class、Collapse Hierarchy。
5.4 死代码(Dead Code)
识别特征:不可达代码;未使用的变量/方法/类;被注释的代码;不可能成立的条件之后的代码。
为什么有害:造成困惑、维护负担、拖慢理解速度。detect-smells.py 的_detect_dead_code()(第 515–539 行)会识别TODO...delete、FIXME...remove、if False:、if (false)等模式。
重构手法:Remove Dead Code、Safe Delete。
检测思路:
# 查找未使用的导出 # 查找未使用的函数 # 留意 IDE 的 "unused" 警告5.5 过度泛化(Speculative Generality)
识别特征:只有一个子类的抽象类;"为将来准备"的未使用参数;只做转发的委托方法;为单一场景搭的"框架"。
为什么有害:无益的复杂度、违反 YAGNI(You Ain't Gonna Need It)、更难理解。
重构手法:Collapse Hierarchy、Inline Class、Remove Parameter、Rename Method。
六、耦合器(Couplers):类与类之间的过度纠缠
6.1 依恋情结(Feature Envy)
识别特征:方法使用另一个类的数据多于自己的;大量调用别的对象的 getter;数据与行为分离。
为什么有害:行为放置位置错误、封装性差、难以维护。
重构手法:Move Method、Move Field、Extract Method(然后移动)。
重构前:
class Order { getDiscountedPrice(customer) { // 大量使用 customer 的数据 if (customer.loyaltyYears > 5) { return this.price * customer.discountRate; } return this.price; } }重构后:
class Customer { getDiscountedPriceFor(price) { if (this.loyaltyYears > 5) { return price * this.discountRate; } return price; } }6.2 过度亲密(Inappropriate Intimacy)
识别特征:类互相访问彼此的私有部分;双向引用;子类对父类了解过多。
为什么有害:高耦合、变更级联、很难独立修改其中一个。
重构手法:Move Method、Move Field、Change Bidirectional to Unidirectional、Extract Class、Hide Delegate。
6.3 消息链(Message Chains)
识别特征:长调用链a.getB().getC().getD().getValue();客户端依赖导航结构;"列车残骸"式代码。
为什么有害:脆弱(任何环节变化都破坏整条链)、违反迪米特法则、与结构强耦合。detect-smells.py 的_detect_message_chains()(第 471–489 行)用正则(\w+(?:\.\w+\([^)]*\)){3,})捕获连续 3 个以上方法调用的链(long_chain_length: 3),标记为 MEDIUM 并建议 Hide Delegate。
重构手法:Hide Delegate、Extract Method、Move Method。
示例:
// 坏:消息链 const managerName = employee.getDepartment().getManager().getName(); // 好:隐藏委托 const managerName = employee.getManagerName();6.4 中间人(Middle Man)
识别特征:类只负责转发给别的类;一半以上方法都是委托;没有附加价值。
为什么有害:不必要的间接层、维护开销、架构混乱。
重构手法:Remove Middle Man、Inline Method。
七、按严重度分级处置:把力气花在刀刃上
坏味道不是都要立即处理。文档给出了四档严重度模型,detect-smells.py 的SmellSeverity枚举(源码第 35–41 行)与报告输出的分级完全对应,且脚本会自动统计 Critical/High/Medium/Low 的数量并给出处置建议:
| 严重度 | 描述 | 行动 |
|---|---|---|
| Critical(严重) | 阻塞开发、引发 Bug | 立即修复 |
| High(高) | 显著的维护负担 | 在当前迭代内修复 |
| Medium(中) | 明显但可控 | 安排到近期工作 |
| Low(低) | 轻微不便 | 顺手修复 |
实际处置优先级可参考 refactor/SKILL.md 的分阶段策略:
- 快速胜利(低风险高价值):重命名变量、抽取明显重复、删除死代码;
- 结构性改进(中风险):从长方法中抽取方法、引入参数对象、把方法移到合适的类;
- 架构性变更(高风险):用多态替换条件、抽取类、引入设计模式。
八、自动化检测:用脚本扫描坏味道
手工扫描难免遗漏,仓库为此打包了两个可执行脚本(Python 3 标准库实现,无第三方依赖),支持 Python / JavaScript / TypeScript 三类文件(扩展名.py/.js/.jsx/.ts/.tsx)。
8.1 坏味道检测器
dectect-smells.py 的用法:
# 分析单个文件 python scripts/detect-smells.py <file> # 分析整个目录 python scripts/detect-smells.py --dir <directory> # 详细模式,附带代码片段 python scripts/detect-smells.py -v <file> # JSON 输出(便于接入 CI 或脚本) python scripts/detect-smells.py -j <file>它检测的坏味道与本文五大家族对应,完整清单(SmellType枚举,源码第 43–58 行)包括:Long Method、Long Parameter List、Duplicate Code、Large Class、Dead Code、Complex Conditional、Magic Number/String、Feature Envy、Excessive Comments、Deeply Nested Code、Primitive Obsession、Data Clumps、Switch Statement、Message Chain。可配置阈值集中在THRESHOLDS字典(第 101–110 行):方法 30/50 行、参数 4 个、大类 300 行/10 方法、嵌套深度 4、链长 3、重复最小 5 行。
8.2 复杂度分析器
analyze-complexity.py 用于量化重构效果,支持三种模式:
# 分析单个文件 python scripts/analyze-complexity.py <file> # 对比重构前/后两个文件 python scripts/analyze-complexity.py <before_file> <after_file> # 分析整个目录 python scripts/analyze-complexity.py --dir <directory>它输出六项指标:
- 圈复杂度(Cyclomatic Complexity):按 McCabe 公式
CC = E - N + 2P的简化实现,统计决策点 + 1; - 认知复杂度(Cognitive Complexity):考虑嵌套深度与控制流中断,衡量理解难度;
- 可维护性指数(Maintainability Index,0–100):基于 Halstead 体量、圈复杂度与 LOC 的简化计算,85+ 高度可维护、65–84 中等、50–64 难以维护、0–49 极难维护;
- 代码行数、函数数量、平均函数长度。
对比模式下脚本会自动给出"可维护性提升/复杂度降低/函数变小"的评估结论(print_comparison(),第 376–439 行),正好对应 refactoring-plan.md 中"重构前后指标对比表"的填写需求。
九、快速自查清单
在扫描代码时逐项过一遍这份清单(源自原文档的完整清单,未删减):
- 是否存在超过 30 行的方法?
- 是否存在超过 300 行的类?
- 是否存在超过 4 个参数的方法?
- 是否存在重复的代码块?
- 是否存在针对类型码的 switch/case?
- 是否存在未使用的代码?
- 是否存在大量使用其他类数据的方法?
- 是否存在过长的调用链?
- 是否存在解释"做了什么"而非"为什么"的注释?
- 是否存在本应是对象的原始类型?
十、坏味道 → 重构手法速查
识别坏味道只是第一步,接下来需要在 refactoring-catalog.md 中选择对应手法(该目录为每个重构都提供了动机、逐步机制与代码示例),并用 refactoring-plan.md 模板组织成"目标 → 坏味道 → 技术 → 步骤 → 风险 → 回滚"的完整计划:
| 坏味道 | 主要重构手法 | 备选手法 |
|---|---|---|
| Long Method | Extract Method | Replace Temp with Query |
| Duplicate Code | Extract Method | Pull Up Method |
| Large Class | Extract Class | Extract Subclass |
| Long Parameter List | Introduce Parameter Object | Preserve Whole Object |
| Feature Envy | Move Method | Extract Method + Move |
| Data Clumps | Extract Class | Introduce Parameter Object |
| Primitive Obsession | Replace Primitive with Object | Replace Type Code |
| Switch Statements | Replace Conditional with Polymorphism | Replace Type Code |
| Temporary Field | Extract Class | Introduce Null Object |
| Message Chains | Hide Delegate | Extract Method |
| Middle Man | Remove Middle Man | Inline Method |
| Divergent Change | Extract Class | Split Phase |
| Shotgun Surgery | Move Method | Inline Class |
| Dead Code | Remove Dead Code | — |
| Speculative Generality | Collapse Hierarchy | Inline Class |
十一、进一步阅读
- refactor/SKILL.md — 六阶段重构工作流:研究 → 测试评估 → 坏味道识别 → 计划 → 增量实施 → 回顾;
- refactoring-catalog.md — 每个坏味道对应的完整重构机制与代码示例;
- refactoring-plan.md — 带风险分级、回滚计划与指标对比表的重构计划模板;
- detect-smells.py — 坏味道自动检测脚本;
- analyze-complexity.py — 复杂度与可维护性度量脚本。
参考书目:Fowler, M. (2018).Refactoring: Improving the Design of Existing Code(2nd ed.);Kerievsky, J. (2004).Refactoring to Patterns;Feathers, M. (2004).Working Effectively with Legacy Code。
【免费下载链接】claude-howtoA visual, example-driven guide to Claude Code — from basic concepts to advanced agents, with copy-paste templates that bring immediate value.项目地址: https://gitcode.com/GitHub_Trending/cl/claude-howto
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考