npx skills add https://github.com/wondelai/skills --skill refactoring-patterns一种通过不改变代码外部行为来改善现有代码内部结构的规范方法。在审查代码、减少技术债务或为新增功能准备代码时,应用这些命名的转换。每次重构都遵循相同的循环:验证测试通过,应用一个小的结构更改,再次验证测试通过。
重构不是重写。它是一系列保持行为不变的小型转换,每一步都有测试支持。 你从不改变代码的功能——你改变的是代码的组织方式。采取微小且已验证的步骤这种规范,正是重构安全性的保障。大刀阔斧的重写之所以失败,是因为它们将结构变更与行为变更混在一起,导致无法分辨是哪个变更破坏了功能。
基础: 糟糕的代码不是性格缺陷——它是在时间压力下交付功能的自然结果。代码异味是结构已经退化的客观信号。命名的重构是修复每种异味的经过验证的机械方法。异味目录告诉你在哪里寻找问题;重构目录告诉你该做什么。
目标:10/10。 在审查或重构代码时,根据对以下原则的遵守情况,从 0 到 10 对结构质量进行评分。10/10 意味着:没有明显的异味残留,每个函数只做一件事,名称揭示意图,重复已被消除,并且测试套件覆盖了重构后的路径。始终提供当前分数以及达到 10/10 所需的具体重构。
系统化改进代码结构的六个关注领域:
核心概念: 代码异味是更深层次结构问题的表面指标。它们不是错误——代码能工作——但它们表明设计使得代码更难以理解、扩展或维护。每种异味都映射到一个或多个可以修复它的命名重构。
为何有效: 如果没有一套共享的异味词汇表,代码审查就会退化为主观的“我不喜欢这个”。命名的异味为团队提供了客观标准:“这是特性依恋——该方法使用了另一个类的六个字段,而只使用了自身的一个字段。” 这个名称直接指向了修复方法。
关键见解:
代码应用:
| 上下文 |
|---|
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 模式 |
|---|
| 示例 |
|---|
| 方法 > 10 行 | 提取方法 | 将循环体提取到 calculateLineTotal() 中 |
| 类 > 200 行 | 提取类 | 将运输逻辑移动到 ShippingCalculator 中 |
| 基于类型代码的 switch | 以多态取代条件表达式 | 为每种订单类型创建子类 |
| 多个方法使用相同参数 | 引入参数对象 | 将 startDate, endDate 分组到 DateRange 中 |
| 方法使用另一个对象的数据 | 移动方法 | 将 calculateDiscount() 移动到 Customer 类 |
| 复制粘贴的逻辑 | 提取方法 + 函数上移 | 通过公共方法或基类共享 |
核心概念: 大多数重构从这里开始。过长的方法被分解成更小、命名良好的片段。每个提取的片段应该只做一件事,并且它的名称应该说明这件事是什么。目标是让方法读起来像散文——一系列高级步骤,每个步骤都委托给一个命名清晰的辅助函数。
为何有效: 具有揭示意图名称的短方法消除了对注释的需求,使错误显而易见(每个方法都足够小,可以一目了然地验证),并实现了重用。当名称告诉你一切时,方法调用的认知成本几乎为零。
关键见解:
代码应用:
| 上下文 | 模式 | 示例 |
|---|---|---|
| 带有注释的代码块 | 提取方法 | // 检查资格 变成 isEligible() |
| 临时变量只使用一次 | 内联变量 | 如果只使用一次,则移除 const price = order.getPrice() |
| 临时变量在多个地方使用 | 以查询取代临时变量 | 用方法调用替换 let discount = getDiscount() |
| 临时变量因不同原因被赋值两次 | 分解临时变量 | 引入 perimeterWidth 和 perimeterHeight |
| 简单的委托方法 | 内联方法 | 如果 moreThanFiveDeliveries() 是 return deliveries > 5 且只使用一次,则内联它 |
| 具有许多局部变量的复杂方法 | 使用方法对象取代方法 | 将方法移到它自己的类中,局部变量变成字段 |
核心概念: 面向对象设计的关键决策是职责放在哪里。当一个方法或字段放错了类——表现为特性依恋、过度耦合或类大小不平衡——就把它移到它所属的地方。
为何有效: 职责放置得当可以减少耦合并增加内聚性。当一个方法位于它使用的数据所在的类中时,对该数据的更改只会影响一个类。放错位置的方法会创建不可见的依赖关系,导致霰弹式修改。
关键见解:
代码应用:
| 上下文 | 模式 | 示例 |
|---|---|---|
| 方法依恋另一个类 | 移动方法 | 将 calculateShipping() 从 Order 移动到 ShippingPolicy |
| 字段被另一个类频繁使用 | 移动字段 | 将 discountRate 从 Order 移动到 Customer |
| 超过 500 行的上帝类 | 提取类 | 将 Address 字段和方法提取到它们自己的类中 |
| 只有一个字段的微小类 | 内联类 | 如果没有行为,将 PhoneNumber 合并回 Contact |
| 客户端调用 a.getB().getC() | 隐藏委托 | 添加 a.getCThroughB() 以便客户端不知道 C |
| 类只转发调用 | 移除中间人 | 让客户端直接调用委托 |
核心概念: 原始数据——魔法数字、暴露的字段、表示为整数的类型代码、平行数组——会产生细微的错误并分散领域知识。用封装行为并强制执行不变量的对象来替换原始表示。
为何有效: 表示货币金额的 int 没有舍入规则、货币代码或格式化的概念。一个 Money 对象封装了所有这些。当领域概念被表示为一等对象时,业务规则集中在一个地方,验证自动发生,类型系统在编译时捕获错误。
关键见解:
EmailAddress、Money、Temperature)Customer 对象,而不是副本)代码应用:
| 上下文 | 模式 | 示例 |
|---|---|---|
if (status == 2) | 以字面常量取代魔法数字 | if (status == ORDER_SHIPPED) |
String email 到处传递 | 以对象取代数据值 | 创建带有验证的 EmailAddress 类 |
| 公共字段 | 封装字段 | 将 order.total 替换为 order.getTotal() |
| Getter 返回可变列表 | 封装集合 | 返回 Collections.unmodifiableList(items) |
带有 switch 的 int typeCode | 以子类取代类型代码 | Employee -> Engineer, Manager, Salesperson |
| 重复的客户记录 | 将值对象改为引用对象 | 通过注册表共享一个 Customer 实例 |
核心概念: 复杂的条件表达式——深度嵌套的 if/else 树、冗长的 switch 语句、到处散落的 null 检查——是最难读的代码,也最可能包含错误。命名的重构将条件表达式分解、合并,并用更清晰的结构替换。
为何有效: 一个包含六个分支和嵌套子条件的条件表达式要求读者在脑海中模拟每条路径。将条件分解为命名良好的方法可以使每个分支自文档化。用多态取代条件表达式可以消除整个类别的“忘记处理这种情况”的错误。
关键见解:
if (x == null) 检查代码应用:
| 上下文 | 模式 | 示例 |
|---|---|---|
带有复杂条件的冗长 if | 分解条件表达式 | 提取 isSummer(date) 和 summerCharge() |
多个 if 返回相同的值 | 合并条件表达式 | 合并到 isDisabled() 中并提前返回 |
深度嵌套的 if/else | 以卫语句取代嵌套条件表达式 | 首先检查边缘情况,提前返回,展平主路径 |
| 基于对象类型的 switch | 以多态取代条件表达式 | 每种类型实现自己的 calculatePay() |
到处是 if (customer == null) | 引入特例 | 创建具有默认行为的 NullCustomer |
| 代码中的隐藏假设 | 引入断言 | 在方法入口处使用 assert quantity > 0 |
核心概念: 重构只有在有测试包裹的情况下才是安全的。工作流是机械的:运行测试(绿色),应用一个小转换,运行测试(绿色),提交。如果测试变红,回滚最后一次更改——不要调试一个失败的重构。
为何有效: 小步骤使得找出问题所在变得微不足道(就是你做的最后一件事)。回滚一个失败的步骤只需要几秒钟。调试一个失败的大刀阔斧重写需要几天。频繁提交创建了可以返回的保存点。
关键见解:
代码应用:
| 上下文 | 模式 | 示例 |
|---|---|---|
| 即将添加功能 | 预备性重构 | 提取方法以使新功能的插入点清晰 |
| 阅读不熟悉的代码 | 理解性重构 | 重命名变量并提取方法以理解意图 |
| 工作时发现小问题 | 随手清理重构 | 在继续之前修复异味(童子军规则) |
| 相同逻辑的第三个副本 | 事不过三原则 | 将共享逻辑提取到公共方法中 |
| 生产环境中的大型 API 变更 | 通过抽象分支 | 引入抽象层,迁移调用者,移除旧路径 |
| 重命名广泛使用的方法 | 并行变更 | 添加新名称,弃用旧名称,迁移调用者,移除旧名称 |
| 错误 | 为何失败 | 修复方法 |
|---|---|---|
| 没有测试就重构 | 没有安全网——无法判断行为是否改变 | 首先编写特征化测试,然后重构 |
| 大刀阔斧重写而非增量步骤 | 结合了结构和行为变更;无法调试 | 采取尽可能小的步骤,每一步后运行测试 |
| 同时进行重构和添加功能 | 同时戴两顶帽子——无法独立验证任一变更 | 分开两顶帽子:先重构(提交),然后添加功能(提交) |
| 重命名而不更新所有调用者 | 破坏构建或引入死代码 | 使用 IDE 的重命名重构;搜索所有引用 |
| 提取过多微小方法 | 当名称不佳时,创建了没有清晰度的间接性 | 每个提取的方法必须有一个名称,使得无需阅读方法体 |
| 忽略异味目录 | 重新发明修复方法,而不是应用经过验证的方案 | 学习命名的异味;每种异味都映射到特定的重构 |
| 重构即将被删除的代码 | 浪费精力——在注定被删除的代码上打磨 | 首先问:这段代码的生命周期是否足够长,值得投入? |
| 在重构期间过早优化 | 混淆了清晰度与性能;优化后的代码通常更难读 | 首先为清晰度重构,然后分析,最后只优化测量到的热点路径 |
| 问题 | 如果否 | 行动 |
|---|---|---|
| 开始前测试通过吗? | 你没有安全网 | 首先编写或修复测试——没有绿色测试不要重构 |
| 你能说出你正在修复的异味名称吗? | 你凭直觉重构,而不是依据目录 | 从目录中识别异味,然后应用其规定的重构 |
| 每个方法大约在 10 行以下吗? | 可能存在过长方法 | 应用提取方法将长方法分解为命名的步骤 |
| 每个类只有一个变更理由吗? | 存在发散式变更或过大类异味 | 应用提取类来分离职责 |
| 有重复的代码块吗? | 重复代码是最昂贵的异味 | 将共享逻辑提取到公共方法或基类中 |
| 条件表达式在适当的地方使用多态吗? | 仍然存在 switch 语句或复杂的 if/else 树 | 应用以多态取代条件表达式 |
| 你在每个重构步骤后提交吗? | 你冒着丢失工作和混淆变更的风险 | 在每次绿色到绿色的转换后提交 |
| 更改后代码更容易阅读吗? | 重构可能增加了复杂性 | 回滚并尝试不同的方法 |
此技能基于改善现有代码设计的权威指南:
Martin Fowler 是 Thoughtworks 的首席科学家,也是软件工程领域最具影响力的声音之一。他是《重构:改善既有代码的设计》(1999 年,第 2 版 2018 年)的作者,该书将基于目录的命名重构转换概念引入了主流软件开发。Fowler 还是《企业应用架构模式》、《UML 精粹》以及众多关于软件设计、敏捷方法和持续交付的有影响力文章的作者。他是《敏捷宣言》的签署者之一,并花费数十年倡导演进式设计——通过规范的、增量的重构而非前期大设计来持续改进代码结构的实践。他的重构目录最初用 Java 编写,现已适应了几乎所有编程语言,并内置在每个主要 IDE 的自动化重构工具中。
每周安装量
240
代码仓库
GitHub 星标数
255
首次出现
2026 年 2 月 23 日
安全审计
安装于
codex231
cursor228
opencode228
gemini-cli227
kimi-cli227
github-copilot227
A disciplined approach to improving the internal structure of existing code without changing its observable behavior. Apply these named transformations when reviewing code, reducing technical debt, or preparing code for new features. Every refactoring follows the same loop: verify tests pass, apply one small structural change, verify tests still pass.
Refactoring is not rewriting. It is a sequence of small, behavior-preserving transformations, each backed by tests. You never change what the code does -- you change how the code is organized. The discipline of taking tiny verified steps is what makes refactoring safe. Big-bang rewrites fail because they combine structural change with behavioral change, making it impossible to know which broke things.
The foundation: Bad code is not a character flaw -- it is a natural consequence of delivering features under time pressure. Code smells are objective signals that structure has degraded. Named refactorings are the proven mechanical recipes for fixing each smell. The catalog of smells tells you where to look; the catalog of refactorings tells you what to do.
Goal: 10/10. When reviewing or refactoring code, rate the structural quality 0-10 based on adherence to the principles below. A 10/10 means: no obvious smells remain, each function does one thing, names reveal intent, duplication is eliminated, and the test suite covers the refactored paths. Always provide the current score and specific refactorings needed to reach 10/10.
Six areas of focus for systematically improving code structure:
Core concept: Code smells are surface indicators of deeper structural problems. They are not bugs -- the code works -- but they signal that the design is making the code harder to understand, extend, or maintain. Each smell maps to one or more named refactorings that fix it.
Why it works: Without a shared vocabulary of smells, code review devolves into subjective "I don't like this." Named smells give teams objective criteria: "This is Feature Envy -- the method uses six fields from another class and only one of its own." The name points directly to the fix.
Key insights:
Code applications:
| Context | Pattern | Example |
|---|---|---|
| Method > 10 lines | Extract Method | Pull the loop body into calculateLineTotal() |
| Class > 200 lines | Extract Class | Move shipping logic into a ShippingCalculator |
| Switch on type code | Replace Conditional with Polymorphism | Create subclasses for each order type |
| Multiple methods use same params | Introduce Parameter Object | Group startDate, endDate into DateRange |
See: references/smell-catalog.md
Core concept: Most refactoring starts here. Long methods are broken into smaller, well-named pieces. Each extracted piece should do one thing and its name should say what that thing is. The goal is methods you can read like prose -- a sequence of high-level steps, each delegating to a clearly named helper.
Why it works: Short methods with intention-revealing names eliminate the need for comments, make bugs obvious (each method is small enough to verify at a glance), and enable reuse. The cognitive cost of a method call is near zero when the name tells you everything.
Key insights:
Code applications:
| Context | Pattern | Example |
|---|---|---|
| Block with a comment | Extract Method | // check eligibility becomes isEligible() |
| Temp used once | Inline Variable | Remove const price = order.getPrice() if used once |
| Temp used in multiple places | Replace Temp with Query | Replace let discount = getDiscount() with method calls |
| Temp assigned twice for different reasons | Split Temporary Variable | Introduce and |
See: references/composing-methods.md
Core concept: The key decision in object-oriented design is where to put responsibilities. When a method or field is in the wrong class -- evidenced by Feature Envy, excessive coupling, or unbalanced class sizes -- move it to where it belongs.
Why it works: Well-placed responsibilities reduce coupling and increase cohesion. When a method lives in the class whose data it uses, changes to that data affect only one class. Misplaced methods create invisible dependencies that cause Shotgun Surgery.
Key insights:
Code applications:
| Context | Pattern | Example |
|---|---|---|
| Method envies another class | Move Method | Move calculateShipping() from Order to ShippingPolicy |
| Field used by another class constantly | Move Field | Move discountRate from Order to Customer |
| God class with 500+ lines | Extract Class | Pull fields and methods into their own class |
See: references/moving-features.md
Core concept: Raw data -- magic numbers, exposed fields, type codes represented as integers, parallel arrays -- creates subtle bugs and scatters domain knowledge. Replace primitive representations with objects that encapsulate behavior and enforce invariants.
Why it works: An int representing a currency amount has no concept of rounding rules, currency codes, or formatting. A Money object encapsulates all of that. When domain concepts are represented as first-class objects, business rules live in one place, validation happens automatically, and the type system catches errors at compile time.
Key insights:
EmailAddress, Money, Temperature)Customer object, not copies)Code applications:
| Context | Pattern | Example |
|---|---|---|
if (status == 2) | Replace Magic Number with Symbolic Constant | if (status == ORDER_SHIPPED) |
String email passed everywhere | Replace Data Value with Object | Create EmailAddress class with validation |
| Public field | Encapsulate Field | Replace order.total with order.getTotal() |
See: references/organizing-data.md
Core concept: Complex conditionals -- deeply nested if/else trees, long switch statements, null checks scattered everywhere -- are the hardest code to read and the most likely to contain bugs. Named refactorings decompose, consolidate, and replace conditionals with clearer structures.
Why it works: A conditional with six branches and nested sub-conditions requires the reader to simulate every path mentally. Decomposing the condition into well-named methods makes each branch self-documenting. Replacing conditionals with polymorphism eliminates entire categories of "forgot to handle this case" bugs.
Key insights:
if (x == null) checks by providing an object that represents "nothing" with safe default behaviorCode applications:
| Context | Pattern | Example |
|---|---|---|
Longif with complex condition | Decompose Conditional | Extract isSummer(date) and summerCharge() |
Multipleifs return same value | Consolidate Conditional | Combine into isDisabled() returning early |
Deeply nestedif/else | Replace with Guard Clauses | Check edge cases first, return early, flatten the main path |
See: references/simplifying-conditionals.md
Core concept: Refactoring is only safe when wrapped in tests. The workflow is mechanical: run tests (green), apply one small transformation, run tests (green), commit. If tests go red, revert the last change -- don't debug a broken refactoring.
Why it works: Small steps make it trivial to find what went wrong (it was the last thing you did). Reverting a failed step costs seconds. Debugging a failed big-bang rewrite costs days. Frequent commits create save points you can return to.
Key insights:
Code applications:
| Context | Pattern | Example |
|---|---|---|
| About to add a feature | Preparatory Refactoring | Extract method to make new feature's insertion point clean |
| Reading unfamiliar code | Comprehension Refactoring | Rename variables and extract methods to understand intent |
| Saw a small issue while working | Litter-Pickup Refactoring | Fix the smell before moving on (Boy Scout Rule) |
| Third copy of same logic | Rule of Three | Extract the shared logic into a common method |
| Large API change in production | Branch by Abstraction | Introduce abstraction layer, migrate callers, remove old path |
| Renaming a widely-used method | Parallel Change | Add new name, deprecate old, migrate callers, remove old |
See: references/refactoring-workflow.md
| Mistake | Why It Fails | Fix |
|---|---|---|
| Refactoring without tests | No safety net -- you can't tell if behavior changed | Write characterization tests first, then refactor |
| Big-bang rewrite instead of incremental steps | Combines structural and behavioral changes; impossible to debug | Take the smallest step possible, run tests after each |
| Refactoring and adding features at the same time | Two hats at once -- you can't verify either change in isolation | Separate the hats: refactor first (commit), then add feature (commit) |
| Renaming without updating all callers | Breaks the build or introduces dead code | Use IDE rename refactoring; search for all references |
| Extracting too many tiny methods | Creates indirection without clarity when names are poor | Each extracted method must have a name that removes the need to read the body |
| Ignoring the smell catalog | Reinventing fixes instead of applying proven recipes | Learn the named smells; each one maps to specific refactorings |
| Refactoring code that will be deleted | Wasted effort -- polish on condemned code | Ask first: is this code's lifespan long enough to justify the investment? |
| Question | If No | Action |
|---|---|---|
| Do tests pass before you start? | You have no safety net | Write or fix tests first -- do not refactor without green tests |
| Can you name the smell you're fixing? | You're refactoring by instinct, not by catalog | Identify the smell from the catalog, then apply its prescribed refactoring |
| Is each method under ~10 lines? | Long Methods are likely present | Apply Extract Method to break long methods into named steps |
| Does each class have a single reason to change? | Divergent Change or Large Class smell | Apply Extract Class to separate responsibilities |
| Are there duplicated code blocks? | Duplicate Code is the most expensive smell | Extract shared logic into a common method or base class |
| Do conditionals use polymorphism where appropriate? | Switch Statements or complex if/else trees remain | Apply Replace Conditional with Polymorphism |
| Are you committing after each refactoring step? |
This skill is based on the definitive guide to improving the design of existing code:
Martin Fowler is the Chief Scientist at Thoughtworks and one of the most influential voices in software engineering. He is the author of Refactoring: Improving the Design of Existing Code (1999, 2nd edition 2018), which introduced the concept of named, catalog-based refactoring transformations to mainstream software development. Fowler is also the author of Patterns of Enterprise Application Architecture , UML Distilled , and numerous influential articles on software design, agile methodology, and continuous delivery. He was a signatory of the Agile Manifesto and has spent decades advocating for evolutionary design -- the practice of continuously improving code structure through disciplined, incremental refactoring rather than upfront big design. His refactoring catalog, originally written in Java, has been adapted to virtually every programming language and is built into the automated refactoring tools of every major IDE.
Weekly Installs
240
Repository
GitHub Stars
255
First Seen
Feb 23, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex231
cursor228
opencode228
gemini-cli227
kimi-cli227
github-copilot227
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
106,200 周安装
| Method uses another object's data |
| Move Method |
Move calculateDiscount() to the Customer class |
| Copy-pasted logic | Extract Method + Pull Up Method | Share via a common method or base class |
perimeterWidthperimeterHeight| Trivial delegating method | Inline Method | Inline moreThanFiveDeliveries() if it's return deliveries > 5 and only used once |
| Complex method with many locals | Replace Method with Method Object | Move the method into its own class where locals become fields |
Address| Tiny class with one field | Inline Class | Merge PhoneNumber back into Contact if no behavior |
| Client calls a.getB().getC() | Hide Delegate | Add a.getCThroughB() so client doesn't know about C |
| Class only forwards calls | Remove Middle Man | Let client call the delegate directly |
| Getter returns mutable list | Encapsulate Collection | Return Collections.unmodifiableList(items) |
int typeCode with switch | Replace Type Code with Subclasses | Employee -> Engineer, Manager, Salesperson |
| Duplicated customer records | Change Value to Reference | Share one Customer instance via a registry |
| Switch on object type | Replace Conditional with Polymorphism | Each type implements its own calculatePay() |
if (customer == null) everywhere | Introduce Special Case | Create NullCustomer with default behavior |
| Hidden assumption in code | Introduce Assertion | assert quantity > 0 at method entry |
| Optimizing prematurely during refactoring | Conflates clarity with performance; optimized code is often harder to read | Refactor for clarity first, then profile, then optimize the measured hot path only |
| You risk losing work and mixing changes |
| Commit after every green-to-green transformation |
| Is the code easier to read after your change? | The refactoring may have added complexity | Revert and try a different approach |