refactor
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRefactor — Expert Code Restructuring
代码重构——专业代码重组
Surgical code refactoring based on Martin Fowler's <Refactoring> (2nd Edition) catalog. Improve structure, readability, and maintainability without changing external behavior. Gradual evolution, not revolution.
基于Martin Fowler《Refactoring》(第二版)目录的精准代码重构。在不改变外部行为的前提下,提升代码结构、可读性和可维护性。采用渐进式演进,而非彻底重写。
When to Use
适用场景
This skill activates when:
- Code is hard to understand or maintain
- Functions/classes have grown too large
- Code smells are detected
- Adding features is difficult due to poor structure
- User explicitly requests refactoring, cleanup, or improvement
- User says: refactor, 重构, clean up, improve code, code smell, extract method, rename, simplify
当出现以下情况时,该技能会激活:
- 代码难以理解或维护
- 函数/类过于庞大
- 检测到代码坏味道
- 因结构不佳导致新增特性困难
- 用户明确要求重构、清理或优化代码
- 用户提及:refactor、重构、clean up、improve code、code smell、extract method、rename、simplify
The Golden Rules
黄金准则
These five rules are non-negotiable. Violating any of them turns refactoring into reckless editing.
以下五条准则不容违背。违反任何一条都会让重构变成鲁莽的代码编辑。
1. Behavior is Preserved
1. 保留原有行为
Only how the code works changes, never what it does. If tests existed before, they must pass after. If the refactoring introduces a behavioral change, it's not refactoring — it's rewriting.
仅改变代码的运行方式,绝不改变其功能。如果重构前已有测试用例,重构后必须全部通过。若重构引入了行为变更,那这不是重构——而是重写。
2. Small Steps
2. 小步迭代
Each change should be the smallest possible transformation that compiles and passes tests. If a step breaks, you know exactly which change caused it. Refactoring is a series of tiny, safe transformations, not one big rewrite.
每次变更都应是最小的可编译且通过测试的转换。如果某一步出错,你能准确知道是哪次变更导致的。重构是一系列微小、安全的转换,而非一次大规模重写。
3. Version Control is Your Friend
3. 版本控制是你的好帮手
Commit before starting. Commit after each successful step. This gives you infinite undo. Branch from a clean state so you can abandon the refactoring without consequences.
开始前提交代码。每完成一步成功的转换后提交代码。这能让你无限次回退。从干净的状态创建分支,这样即使放弃重构也不会造成影响。
4. Tests are Essential
4. 测试必不可少
"Without tests, you're not refactoring — you're just editing." If tests don't exist for the target code, write characterization tests first. These tests capture the current behavior so you can detect regressions.
“没有测试,你不是在重构——只是在编辑代码。”如果目标代码没有测试用例,先编写特征测试。这类测试会捕获当前行为,以便你检测回归问题。
5. One Thing at a Time
5. 一次只做一件事
Never mix refactoring with feature changes. Never refactor two unrelated things simultaneously. Each commit should contain exactly one refactoring operation.
永远不要将重构与特性变更混在一起。永远不要同时重构两个不相关的内容。每次提交应只包含一项重构操作。
When NOT to Refactor
不适宜重构的场景
| Scenario | Action |
|---|---|
| Code works and won't change again | Leave it alone |
| Critical production path with no tests | Write characterization tests first |
| Under tight deadline pressure | Document the smell, refactor later |
| No clear purpose or benefit | Don't refactor for refactoring's sake |
| Code is fundamentally wrong | This is a rewrite, not a refactoring |
| 场景 | 应对措施 |
|---|---|
| 代码正常运行且不会再变更 | 保持原样 |
| 关键生产路径无测试用例 | 先编写特征测试 |
| 面临紧迫的截止日期 | 记录代码坏味道,后续再重构 |
| 无明确目的或收益 | 不要为了重构而重构 |
| 代码存在根本性错误 | 这属于重写,而非重构 |
Code Smells Catalog
代码坏味道目录
Based on Fowler's taxonomy. Before refactoring, identify which smell is present.
基于Fowler的分类体系。重构前,先识别存在的坏味道类型。
Bloaters
臃肿类
| Smell | Description | Primary Refactoring |
|---|---|---|
| Long Method | Method > 10-15 lines, doing multiple things | Extract Method, Replace Temp with Query |
| Large Class | Class with too many fields/methods (God Object) | Extract Class, Extract Subclass |
| Primitive Obsession | Using primitives instead of small objects | Replace Data Value with Object, Replace Type Code with Class |
| Long Parameter List | Method with > 3-4 parameters | Introduce Parameter Object, Preserve Whole Object |
| Data Clumps | Same group of data appearing together | Extract Class, Introduce Parameter Object |
| 坏味道 | 描述 | 主要重构手法 |
|---|---|---|
| Long Method | 方法超过10-15行,承担多项职责 | Extract Method、Replace Temp with Query |
| Large Class | 包含过多字段/方法的类(上帝对象) | Extract Class、Extract Subclass |
| Primitive Obsession | 使用基本类型而非小型对象 | Replace Data Value with Object、Replace Type Code with Class |
| Long Parameter List | 方法参数超过3-4个 | Introduce Parameter Object、Preserve Whole Object |
| Data Clumps | 同一组数据重复出现 | Extract Class、Introduce Parameter Object |
Object-Orientation Abusers
面向对象滥用
| Smell | Description | Primary Refactoring |
|---|---|---|
| Switch Statements | Repeated switch/if-else on type codes | Replace Conditional with Polymorphism, Replace Type Code with Subclasses |
| Temporary Field | Field only set in certain circumstances | Extract Class, Introduce Null Object |
| Refused Bequest | Subclass doesn't use inherited members | Replace Inheritance with Delegation, Push Down Method/Field |
| Alternative Classes with Different Interfaces | Classes doing similar things with different names | Rename Method, Move Method, Extract Superclass |
| 坏味道 | 描述 | 主要重构手法 |
|---|---|---|
| Switch Statements | 重复基于类型码的switch/if-else语句 | Replace Conditional with Polymorphism、Replace Type Code with Subclasses |
| Temporary Field | 仅在特定场景下被赋值的字段 | Extract Class、Introduce Null Object |
| Refused Bequest | 子类未使用继承的成员 | Replace Inheritance with Delegation、Push Down Method/Field |
| Alternative Classes with Different Interfaces | 功能相似但接口不同的类 | Rename Method、Move Method、Extract Superclass |
Change Preventers
变更阻碍
| Smell | Description | Primary Refactoring |
|---|---|---|
| Divergent Change | One class changed for different reasons | Extract Class |
| Shotgun Surgery | One change requires many small changes across classes | Move Method, Move Field, Inline Class |
| Parallel Inheritance Hierarchies | Adding a subclass to one hierarchy forces adding to another | Move Method, Move Field |
| 坏味道 | 描述 | 主要重构手法 |
|---|---|---|
| Divergent Change | 一个类因不同原因被修改 | Extract Class |
| Shotgun Surgery | 一项变更需要在多个类中进行多处小修改 | Move Method、Move Field、Inline Class |
| Parallel Inheritance Hierarchies | 在一个层级中添加子类会强制在另一个层级也添加子类 | Move Method、Move Field |
Dispensables
冗余代码
| Smell | Description | Primary Refactoring |
|---|---|---|
| Comments | Comments explaining what code does (not why) | Extract Method, Rename Variable, Introduce Assertion |
| Duplicate Code | Same code structure in multiple places | Extract Method, Pull Up Method, Form Template Method |
| Lazy Class | Class doing too little to justify existence | Inline Class, Collapse Hierarchy |
| Data Class | Class with only fields and getters/setters | Move Method, Encapsulate Field, Encapsulate Collection |
| Dead Code | Unused code, imports, commented-out blocks | Delete it (git history has it) |
| Speculative Generality | Code built for "someday" that never came | Inline Class, Collapse Hierarchy, Remove Parameter |
| 坏味道 | 描述 | 主要重构手法 |
|---|---|---|
| Comments | 解释代码功能(而非原因)的注释 | Extract Method、Rename Variable、Introduce Assertion |
| Duplicate Code | 多个位置存在相同代码结构 | Extract Method、Pull Up Method、Form Template Method |
| Lazy Class | 职责过少、无存在必要的类 | Inline Class、Collapse Hierarchy |
| Data Class | 仅包含字段和getter/setter的类 | Move Method、Encapsulate Field、Encapsulate Collection |
| Dead Code | 未使用的代码、导入语句、注释掉的代码块 | 删除(git历史中会保留) |
| Speculative Generality | 为“未来可能用到”而编写但从未使用的代码 | Inline Class、Collapse Hierarchy、Remove Parameter |
Couplers
耦合类
| Smell | Description | Primary Refactoring |
|---|---|---|
| Feature Envy | Method uses another class's data more than its own | Move Method, Extract Method + Move Method |
| Inappropriate Intimacy | Classes know too much about each other's internals | Move Method, Move Field, Replace Delegation with Hidden Delegate |
| Message Chains | | Hide Delegate, Extract Method |
| Middle Man | Class delegates everything to another class | Remove Middle Man, Inline Method |
| Incomplete Library Class | Library missing methods you need | Introduce Foreign Method, Introduce Local Extension |
| 坏味道 | 描述 | 主要重构手法 |
|---|---|---|
| Feature Envy | 方法使用其他类的数据多于自身类的数据 | Move Method、Extract Method + Move Method |
| Inappropriate Intimacy | 类之间过度了解彼此的内部实现 | Move Method、Move Field、Replace Delegation with Hidden Delegate |
| Message Chains | | Hide Delegate、Extract Method |
| Middle Man | 类将所有职责委托给另一个类 | Remove Middle Man、Inline Method |
| Incomplete Library Class | 库缺少你需要的方法 | Introduce Foreign Method、Introduce Local Extension |
Refactoring Techniques Catalog
重构手法目录
Organized by category, from Fowler's catalog. Each technique includes its mechanical steps.
按类别整理,源自Fowler的目录。每种手法包含具体操作步骤。
Composing Methods
方法组合
Extract Method
Extract Method
Turn a code fragment into a method whose name explains its purpose.
Mechanics:
- Create a new method named after what the fragment does (not how)
- Copy the extracted code into the new method
- Identify local variables: read-only become parameters, modified become return values
- Pass parameters and handle return values
- Replace the original fragment with a call to the new method
- Test
Before:
java
void printOwing() {
printBanner();
// Print details
System.out.println("name: " + _name);
System.out.println("amount: " + getOutstanding());
}After:
java
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails(double outstanding) {
System.out.println("name: " + _name);
System.out.println("amount: " + outstanding);
}将代码片段转换为一个名称能解释其用途的方法。
操作步骤:
- 创建一个以片段用途命名的新方法(而非实现方式)
- 将提取的代码复制到新方法中
- 识别局部变量:只读变量作为参数,修改后的变量作为返回值
- 传递参数并处理返回值
- 用新方法的调用替换原代码片段
- 测试
重构前:
java
void printOwing() {
printBanner();
// Print details
System.out.println("name: " + _name);
System.out.println("amount: " + getOutstanding());
}重构后:
java
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails(double outstanding) {
System.out.println("name: " + _name);
System.out.println("amount: " + outstanding);
}Inline Method
Inline Method
Replace a method call with its body when the method body is as clear as the name.
Mechanics:
- Check the method is not polymorphic (no subclasses override it)
- Find all callers
- Replace each call with the method body
- Delete the method definition
- Test
当方法体和方法名称一样清晰时,用方法体替换方法调用。
操作步骤:
- 确认该方法不是多态方法(没有子类重写它)
- 找到所有调用者
- 将每个调用替换为方法体
- 删除方法定义
- 测试
Extract Variable
Extract Variable
Put the result of an expression (or part of it) in a self-explanatory variable.
Before:
java
if (platform.toUpperCase().indexOf("MAC") > -1 &&
browser.toUpperCase().indexOf("IE") > -1 &&
wasInitialized() && resize > 0) {
// ...
}After:
java
final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") > -1;
final boolean wasResized = resize > 0;
if (isMacOs && isIEBrowser && wasInitialized() && wasResized) {
// ...
}将表达式(或其一部分)的结果赋值给一个自解释的变量。
重构前:
java
if (platform.toUpperCase().indexOf("MAC") > -1 &&
browser.toUpperCase().indexOf("IE") > -1 &&
wasInitialized() && resize > 0) {
// ...
}重构后:
java
final boolean isMacOs = platform.toUpperCase().indexOf("MAC") > -1;
final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") > -1;
final boolean wasResized = resize > 0;
if (isMacOs && isIEBrowser && wasInitialized() && wasResized) {
// ...
}Inline Temp
Inline Temp
Replace a temp variable with its expression when the temp is only used once and the expression is clear.
当临时变量仅被使用一次且表达式清晰时,用表达式替换临时变量。
Replace Temp with Query
Replace Temp with Query
Extract the expression into a method. Temps that are computed once and reused are replaced with method calls.
将表达式提取为方法。将计算一次并重复使用的临时变量替换为方法调用。
Split Temporary Variable
Split Temporary Variable
A temp assigned more than once (not loop/collecting) should be split into separate variables, one per responsibility.
被多次赋值(非循环/收集场景)的临时变量应拆分为多个变量,每个变量对应一项职责。
Remove Assignments to Parameters
Remove Assignments to Parameters
Don't assign to parameters. Use a local variable instead.
不要给参数赋值,改用局部变量。
Replace Method with Method Object
Replace Method with Method Object
When a long method uses many local variables that make Extract Method hard, turn the method into its own class, with locals as fields.
当长方法使用过多局部变量导致Extract Method难以实施时,将该方法转换为独立类,局部变量作为类的字段。
Substitute Algorithm
Substitute Algorithm
Replace an algorithm with a clearer one.
用更清晰的算法替换原有算法。
Moving Features Between Objects
对象间特性迁移
Move Method
Move Method
Move a method to the class where it's used most.
Mechanics:
- Check all features used by the method on its current class
- Check for polymorphism (subclass/superclass methods)
- Create the method on the target class, adapting as needed
- Reference the target object from the source
- Turn the source method into a delegating method, or remove it
- Test
将方法迁移到使用它最多的类中。
操作步骤:
- 检查当前类中该方法使用的所有特性
- 检查是否存在多态(子类/父类方法)
- 在目标类中创建该方法并按需调整
- 在源类中引用目标对象
- 将源方法转换为委托方法,或直接删除
- 测试
Move Field
Move Field
Move a field to the class where it's used most.
将字段迁移到使用它最多的类中。
Extract Class
Extract Class
When a class does the work of two, split it. Create a new class and move relevant fields and methods.
当一个类承担了两个类的职责时,拆分它。创建一个新类并迁移相关字段和方法。
Inline Class
Inline Class
When a class does almost nothing, absorb it into the class that uses it most.
当一个类几乎没有职责时,将其合并到使用它最多的类中。
Hide Delegate
Hide Delegate
Create methods on the server to hide the delegate chain. → .
manager = person.getDepartment().getManager()manager = person.getManager()在服务类中创建方法以隐藏委托链。例如将改为。
manager = person.getDepartment().getManager()manager = person.getManager()Remove Middle Man
Remove Middle Man
When a class is doing too much delegation, call the delegate directly.
当一个类过度委托时,直接调用委托类。
Introduce Foreign Method
Introduce Foreign Method
When a server class needs an additional method but you can't modify it, create a method on the client with the server instance as the first argument.
当服务类需要额外方法但无法修改它时,在客户端类中创建一个以服务实例为第一个参数的方法。
Introduce Local Extension
Introduce Local Extension
When you need multiple foreign methods, create an extension class (subclass or wrapper).
当需要多个外部方法时,创建一个扩展类(子类或包装类)。
Organizing Data
数据组织
Self Encapsulate Field
Self Encapsulate Field
Access fields through getters and setters, even within the owning class.
即使在所属类内部,也通过getter和setter访问字段。
Replace Data Value with Object
Replace Data Value with Object
When a data item needs additional data or behavior, turn it into an object.
Before:
java
class Order {
private String customer; // Just a string
}After:
java
class Order {
private Customer customer; // Rich object with name, address, credit rating
}当数据项需要额外数据或行为时,将其转换为对象。
重构前:
java
class Order {
private String customer; // Just a string
}重构后:
java
class Order {
private Customer customer; // 包含名称、地址、信用评级的富对象
}Change Value to Reference
Change Value to Reference
When you need to share one instance of an object across multiple places.
当需要在多个地方共享同一个对象实例时使用。
Change Reference to Value
Change Reference to Value
When a reference object is small, immutable, and you want value semantics.
当引用对象很小、不可变且需要值语义时使用。
Replace Array with Object
Replace Array with Object
When an array holds heterogeneous data ( — name, score, wins), replace with an object.
String[] row = new String[3]当数组存储异构数据时(例如——名称、分数、获胜次数),用对象替换数组。
String[] row = new String[3]Replace Magic Number with Symbolic Constant
Replace Magic Number with Symbolic Constant
Replace literal numbers/strings with named constants.
用命名常量替换字面量数字/字符串。
Encapsulate Field
Encapsulate Field
Make public fields private and provide accessors.
将公共字段设为私有并提供访问器。
Encapsulate Collection
Encapsulate Collection
Never return the raw collection. Return a read-only view and provide add/remove methods.
永远不要返回原始集合。返回只读视图并提供添加/删除方法。
Replace Type Code with Class
Replace Type Code with Class
Replace a numeric/string type code with a class that has meaningful behavior.
用具有有意义行为的类替换数值/字符串类型码。
Replace Type Code with Subclasses
Replace Type Code with Subclasses
When type code affects behavior, use polymorphism instead of conditionals.
当类型码影响行为时,用多态替代条件判断。
Replace Type Code with State/Strategy
Replace Type Code with State/Strategy
Similar to subclasses but uses composition when the type can change at runtime.
与子类方式类似,但当类型可在运行时变更时使用组合方式。
Replace Subclass with Fields
Replace Subclass with Fields
When subclasses vary only in constant data, replace them with fields on a single class.
当子类仅在常量数据上存在差异时,用单个类的字段替换子类。
Simplifying Conditional Expressions
条件表达式简化
Decompose Conditional
Decompose Conditional
Extract the condition, then-part, and else-part into separate methods.
Before:
java
if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
charge = quantity * _winterRate + _winterServiceCharge;
} else {
charge = quantity * _summerRate;
}After:
java
if (isSummer(date)) {
charge = summerCharge(quantity);
} else {
charge = winterCharge(quantity);
}将条件、then分支和else分支提取为独立方法。
重构前:
java
if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
charge = quantity * _winterRate + _winterServiceCharge;
} else {
charge = quantity * _summerRate;
}重构后:
java
if (isSummer(date)) {
charge = summerCharge(quantity);
} else {
charge = winterCharge(quantity);
}Consolidate Conditional Expression
Consolidate Conditional Expression
Combine multiple conditionals that have the same result.
合并多个结果相同的条件判断。
Consolidate Duplicate Conditional Fragments
Consolidate Duplicate Conditional Fragments
Move code that appears in every branch outside the conditional.
将所有分支中重复的代码移到条件判断外部。
Remove Control Flag
Remove Control Flag
Replace control flags with break, continue, or return.
用break、continue或return替换控制标志。
Replace Nested Conditional with Guard Clauses
Replace Nested Conditional with Guard Clauses
Use early returns for special cases instead of deep nesting.
Before (arrow code):
java
double getPayAmount() {
double result;
if (_isDead) {
result = deadAmount();
} else {
if (_isSeparated) {
result = separatedAmount();
} else {
if (_isRetired) {
result = retiredAmount();
} else {
result = normalPayAmount();
}
}
}
return result;
}After:
java
double getPayAmount() {
if (_isDead) return deadAmount();
if (_isSeparated) return separatedAmount();
if (_isRetired) return retiredAmount();
return normalPayAmount();
}对特殊情况使用提前返回,替代深层嵌套。
重构前(箭头代码):
java
double getPayAmount() {
double result;
if (_isDead) {
result = deadAmount();
} else {
if (_isSeparated) {
result = separatedAmount();
} else {
if (_isRetired) {
result = retiredAmount();
} else {
result = normalPayAmount();
}
}
}
return result;
}重构后:
java
double getPayAmount() {
if (_isDead) return deadAmount();
if (_isSeparated) return separatedAmount();
if (_isRetired) return retiredAmount();
return normalPayAmount();
}Replace Conditional with Polymorphism
Replace Conditional with Polymorphism
When a conditional chooses different behavior based on the type of an object, use subclasses.
当条件判断基于对象类型选择不同行为时,使用子类。
Introduce Null Object
Introduce Null Object
Replace null checks with a null object that provides default behavior.
用提供默认行为的空对象替换空值检查。
Introduce Assertion
Introduce Assertion
State assumptions explicitly with assertions.
用断言明确声明假设。
Making Method Calls Simpler
方法调用简化
Rename Method
Rename Method
The name should say what the method does. If you can't think of a good name, the method may have multiple responsibilities.
方法名称应说明其功能。如果你想不出合适的名称,说明该方法可能承担了多项职责。
Add Parameter / Remove Parameter
Add Parameter / Remove Parameter
Add parameters when a method needs more info. Remove parameters when the method can get the info another way.
当方法需要更多信息时添加参数。当方法可通过其他方式获取信息时移除参数。
Separate Query from Modifier
Separate Query from Modifier
A method should either return a value OR change state, never both.
方法应要么返回值,要么改变状态,不可两者兼具。
Parameterize Method
Parameterize Method
Several methods doing similar things with different values → one method with a parameter.
将多个功能相似但参数不同的方法合并为一个带参数的方法。
Replace Parameter with Explicit Methods
Replace Parameter with Explicit Methods
The inverse: when a parameter essentially selects different behavior, create separate methods.
反向操作:当参数本质上是选择不同行为时,创建独立方法。
Preserve Whole Object
Preserve Whole Object
Pass the whole object instead of pulling individual fields from it.
传递整个对象,而非从中提取单个字段。
Replace Parameter with Method
Replace Parameter with Method
When a parameter can be computed from data the object already has.
当参数可从对象已有数据计算得出时使用。
Introduce Parameter Object
Introduce Parameter Object
Group parameters that naturally go together into an object.
将自然关联的参数组合成一个对象。
Remove Setting Method
Remove Setting Method
Make a field immutable by removing its setter and setting it in the constructor.
通过移除setter并在构造函数中初始化字段,使字段不可变。
Hide Method
Hide Method
Make methods private when they're not used outside the class.
当方法未在类外部使用时,设为私有。
Replace Constructor with Factory Method
Replace Constructor with Factory Method
When you need more flexibility than a simple constructor call.
当需要比简单构造函数调用更多灵活性时使用。
Replace Error Code with Exception
Replace Error Code with Exception
Throw an exception instead of returning an error code.
抛出异常而非返回错误码。
Replace Exception with Test
Replace Exception with Test
Check the condition first instead of catching an exception.
先检查条件而非捕获异常。
Dealing with Generalization
泛化处理
Pull Up Field/Method/Constructor Body
Pull Up Field/Method/Constructor Body
Move identical fields/methods/constructor code from subclasses to superclass.
将子类中相同的字段/方法/构造函数代码移到父类。
Push Down Method/Field
Push Down Method/Field
Move behavior from superclass to only the subclasses that use it.
将父类中的行为移到仅使用它的子类中。
Extract Subclass
Extract Subclass
Create a subclass for a subset of features used in some instances.
为部分实例使用的特性子集创建子类。
Extract Superclass
Extract Superclass
Create a superclass for shared features of similar classes.
为相似类的共享特性创建父类。
Extract Interface
Extract Interface
Create an interface from a subset of a class's public methods.
从类的公共方法子集中创建接口。
Collapse Hierarchy
Collapse Hierarchy
Merge a superclass and subclass when they're not different enough.
当父类和子类差异不大时,合并它们。
Form Template Method
Form Template Method
Generalize an algorithm in the superclass, letting subclasses fill in the specifics.
在父类中提取通用算法框架,让子类填充具体细节。
Replace Inheritance with Delegation
Replace Inheritance with Delegation
When a subclass only uses part of the superclass, use composition instead.
当子类仅使用父类的部分功能时,用组合替代继承。
Replace Delegation with Inheritance
Replace Delegation with Inheritance
When a delegating class needs access to all of the delegate's behavior.
当委托类需要访问委托对象的所有行为时使用。
The Refactoring Process
重构流程
Phase 1: Prepare
阶段1:准备
- Write characterization tests if they don't exist. These capture current behavior — they don't need to be elegant, just comprehensive enough to catch regressions.
- Commit current state. Start from a clean working tree.
- Create a branch for the refactoring. Keep it separate from feature work.
- 编写特征测试(如果不存在)。这类测试捕获当前行为——不需要优雅,只需足够全面以检测回归问题。
- 提交当前状态。从干净的工作区开始。
- 创建分支用于重构。与功能开发分离。
Phase 2: Identify
阶段2:识别
- Smell the code. Use the smell catalog above to classify what's wrong.
- Understand the code. Read it thoroughly. You must understand what it does before changing it.
- Choose the right refactoring. Pick from the technique catalog. Know what the result looks like before you start.
- 识别代码坏味道。使用上述坏味道目录分类问题。
- 理解代码。彻底阅读代码。在修改前必须理解其功能。
- 选择合适的重构手法。从手法目录中挑选。开始前要明确预期结果。
Phase 3: Refactor (Small Steps)
阶段3:重构(小步迭代)
For each step:
- Make one small change. One refactoring technique at a time.
- Compile. The code should compile after every change.
- Run tests. All tests must pass. If they don't, you've changed behavior.
- Commit. Create a commit with a message like .
refactor: extract validateEmail method
Repeat until the smell is resolved.
每一步操作:
- 做出一个小变更。一次使用一种重构手法。
- 编译。每次变更后代码必须能编译通过。
- 运行测试。所有测试必须通过。如果未通过,说明你改变了行为。
- 提交。提交信息格式如:。
refactor: extract validateEmail method
重复操作直到坏味道解决。
Phase 4: Verify
阶段4:验证
- All tests pass. Non-negotiable.
- Manual check. Briefly run the application or review the diff for unintended changes.
- Performance. Ensure no performance regression. Simple refactorings rarely cause them, but check.
- 所有测试通过。这是硬性要求。
- 手动检查。快速运行应用或查看差异,确认无意外变更。
- 性能检查。确保没有性能退化。简单重构很少导致性能问题,但仍需检查。
Phase 5: Clean Up
阶段5:清理
- Remove stale comments. If a refactoring made a comment obvious, delete the comment.
- Check for dead code. After refactorings, unused code may emerge.
- Final commit. Summarize the refactoring sequence.
- 移除过时注释。如果重构让注释变得多余,删除注释。
- 检查冗余代码。重构后可能出现未使用的代码。
- 最终提交。总结重构过程。
Refactoring Checklist
重构检查清单
Code Quality
代码质量
- Functions are small (< 20 lines preferred, < 50 lines max)
- Each function does one thing (single responsibility)
- No duplicated code (DRY)
- Names describe what, not how
- No magic numbers or strings
- Dead code removed
- 函数体量小(推荐少于20行,最多不超过50行)
- 每个函数只做一件事(单一职责)
- 无重复代码(DRY原则)
- 名称描述功能,而非实现方式
- 无魔法数字或字符串
- 已移除冗余代码
Structure
结构
- Related code is grouped together
- Module boundaries are clear
- Dependencies flow in one direction (no cycles)
- No circular dependencies
- 相关代码分组存放
- 模块边界清晰
- 依赖单向流动(无循环)
- 无循环依赖
Conditionals
条件判断
- Guard clauses replace deep nesting
- Complex conditions extracted to named methods
- Polymorphism replaces type-switching conditionals
- Null Object pattern where appropriate
- 使用卫语句替代深层嵌套
- 复杂条件已提取为命名方法
- 用多态替代基于类型的分支判断
- 合理使用Null Object模式
Type Safety (typed languages)
类型安全(强类型语言)
- Types defined for all public APIs
- No usage without qualification
any - Nullable types explicitly marked
- Type codes replaced with classes/enums
- 所有公共API都定义了类型
- 无无理由的类型使用
any - 可空类型已明确标记
- 类型码已替换为类/枚举
Testing
测试
- Refactored code is tested
- Edge cases are covered
- All tests pass after each step
- Characterization tests capture pre-refactoring behavior
- 重构后的代码已测试
- 覆盖了边缘情况
- 每一步后所有测试都通过
- 特征测试捕获了重构前的行为
Language-Specific Guidance
语言特定指导
Java
Java
- Prefer for locals that shouldn't change
final - Use IDE automated refactorings (Eclipse/IntelliJ) for mechanical steps
- Leverage the type system: enums, records (Java 14+), sealed classes (Java 17+)
- 对不应变更的局部变量优先使用
final - 机械步骤使用IDE自动重构(Eclipse/IntelliJ)
- 利用类型系统:枚举、records(Java 14+)、密封类(Java 17+)
JavaScript/TypeScript
JavaScript/TypeScript
- Use destructuring to reduce parameter count
- Prefer over
constfor immutable bindingslet - Use TypeScript union types instead of type codes
- Nullish coalescing () and optional chaining (
??) eliminate null-check noise?.
- 使用解构减少参数数量
- 对不可变绑定优先使用而非
constlet - 用TypeScript联合类型替代类型码
- 空值合并运算符()和可选链(
??)消除空值检查冗余?.
Python
Python
- Use type hints for documenting intent during refactoring
- Use to replace tuple/data-class patterns
dataclasses - Use to replace getters
@property - Context managers for resource cleanup patterns
- 重构期间使用类型提示记录意图
- 用替换元组/数据类模式
dataclasses - 用替换getter
@property - 用上下文管理器处理资源清理模式
Go
Go
- Small interfaces preferred: accept interfaces, return structs
- Use named return values when they improve clarity
- Table-driven tests pair well with refactoring
- Avoid deep nesting with early returns
- 偏好小型接口:接受接口,返回结构体
- 当能提升清晰度时使用命名返回值
- 表驱动测试与重构适配性好
- 用提前返回避免深层嵌套
Rust
Rust
- Use and
Resultinstead of error codes and nullOption - Pattern matching replaces if-else chains
- trait implementations clean up type conversions
From - Derive macros reduce boilerplate
- 用和
Result替代错误码和空值Option - 用模式匹配替代if-else链
- trait实现简化类型转换
From - 派生宏减少样板代码
Common Refactoring Sequences
常见重构序列
Extract Method Sequence
Extract Method序列
- Create a new method named after intent
- Copy code fragment into new method
- Identify local variables → parameters / return values
- Call new method from original location
- Test
- 创建一个以意图命名的新方法
- 将代码片段复制到新方法
- 识别局部变量 → 参数/返回值
- 在原位置调用新方法
- 测试
Replace Conditional with Polymorphism Sequence
Replace Conditional with Polymorphism序列
- Create subclasses for each variant
- Create a factory method that returns the right subclass
- Move the conditional body to the appropriate subclass method
- Delete the conditional
- 为每个变体创建子类
- 创建返回对应子类的工厂方法
- 将条件判断体移到对应子类方法中
- 删除条件判断
Extract Class Sequence
Extract Class序列
- Identify a coherent subset of fields and methods
- Create a new class
- Create an instance from the old class
- Move fields and methods one at a time
- Update references in old class
- Test after each move
- 识别一组相关的字段和方法
- 创建新类
- 在旧类中创建新类的实例
- 逐个迁移字段和方法
- 更新旧类中的引用
- 每次迁移后测试
Inline Class Sequence
Inline Class序列
- Identify all callers of the target class
- Move all methods/fields to the absorbing class
- Redirect all references to the absorbing class
- Delete the empty class
- Test
- 识别目标类的所有调用者
- 将所有方法/字段移到合并类中
- 将所有引用重定向到合并类
- 删除空类
- 测试
Safety Protocol
安全规范
Before You Touch Anything
动手前准备
1. Characterization tests → capture what the code does now
2. Git commit → save a known-good state
3. Branch → isolate refactoring from other work1. 特征测试 → 捕获当前代码行为
2. Git提交 → 保存已知的良好状态
3. 创建分支 → 将重构与其他工作隔离Every Single Step
每一步操作
1. One change → one refactoring technique
2. Compile → must compile clean
3. Tests → every test must pass
4. Commit → message: "refactor: <technique> <what>"1. 单一变更 → 一次使用一种重构手法
2. 编译 → 必须编译通过
3. 测试 → 所有测试必须通过
4. 提交 → 信息格式:"refactor: <手法> <内容>"If Tests Break
若测试失败
1. Undo the last change
2. Understand what broke and why
3. Try a smaller step
4. If the test was wrong and behavior was correct, fix the test FIRST, then retry1. 撤销最后一次变更
2. 理解失败原因
3. 尝试更小的步骤
4. 如果测试错误但行为正确,先修复测试,再重试On Completion
完成后
1. Full test suite → all tests pass
2. Manual smoke test → quick sanity check
3. Self-review diff → catch unintended changes
4. Final commit → describe the overall transformation1. 完整测试套件 → 所有测试通过
2. 手动冒烟测试 → 快速 sanity 检查
3. 自查代码差异 → 发现意外变更
4. 最终提交 → 描述整体转换过程Design Patterns in Refactoring
重构中的设计模式
Strategy Pattern
策略模式
Replace a conditional that chooses an algorithm. Smell: Switch on type code with different behavior per branch. Technique: Replace Conditional with Polymorphism + Extract Method.
替换选择算法的条件判断。坏味道: 基于类型码的分支判断,每个分支行为不同。手法: Replace Conditional with Polymorphism + Extract Method。
Template Method
模板方法模式
Extract common algorithm skeleton to superclass, letting subclasses fill in the variants. Smell: Duplicate code with slight variations. Technique: Form Template Method.
将通用算法框架提取到父类,让子类填充变体。坏味道: 存在细微差异的重复代码。手法: Form Template Method。
State Pattern
状态模式
Replace a state-based conditional by extracting each state's behavior into a class. Smell: Switch on status field with behavior variation. Technique: Replace Type Code with State/Strategy.
通过将每个状态的行为提取到类中,替换基于状态的条件判断。坏味道: 基于状态字段的分支判断,行为随状态变化。手法: Replace Type Code with State/Strategy。
Composite Pattern
组合模式
Treat individual objects and groups uniformly. Smell: Client code has special handling for single vs. collection cases. Technique: Extract Interface + Create Composite.
统一处理单个对象和对象组。坏味道: 客户端代码对单个对象和集合有特殊处理。手法: Extract Interface + Create Composite。
Decorator Pattern
装饰器模式
Add behavior dynamically by wrapping objects. Smell: Conditional logic for optional behaviors. Technique: Extract Class + use composition.
通过包装对象动态添加行为。坏味道: 针对可选行为的条件逻辑。手法: Extract Class + 使用组合。
Null Object Pattern
空对象模式
Replace null checks with a default object. Smell: Repeated checks. Technique: Introduce Null Object.
if (x == null)用默认对象替换空值检查。坏味道: 重复的检查。手法: Introduce Null Object。
if (x == null)Edge Cases & Gotchas
边缘情况与注意事项
| Scenario | Handling |
|---|---|
| No tests exist | Write characterization tests first. Run the code with various inputs, capture outputs. These are your safety net. |
| Refactoring breaks a distant test | FIRST understand why. Maybe the test relied on implementation detail. If so, fix the test to test behavior, not implementation. Then resume. |
| User wants behavior change + refactor together | REFUSE. Do them separately. Refactor first to make the behavior change easy, commit, then change behavior. |
| Method is too complex to step through | Use Replace Method with Method Object. Turn the whole method into a class where each step can be extracted. |
| Refactoring across a large codebase | Extract a micro-service or module boundary first. Then refactor within the boundary. "There is a refactoring for everything except too many refactorings." |
| IDE automated refactoring available | Use it. Modern IDEs can safely rename, extract method, introduce variable, etc. Only do it manually when the IDE can't. |
| Undo needed | |
| 场景 | 处理方式 |
|---|---|
| 无测试用例 | 先编写特征测试。用各种输入运行代码,捕获输出。这是你的安全保障。 |
| 重构破坏了远程测试 | 首先理解原因。可能测试依赖于实现细节。如果是这样,先修复测试使其测试行为而非实现,再继续重构。 |
| 用户要求同时变更行为和重构 | 拒绝。分开进行。先重构以便于行为变更,提交后再修改行为。 |
| 方法过于复杂无法逐步处理 | 使用Replace Method with Method Object。将整个方法转换为类,每个步骤都可提取。 |
| 跨大型代码库重构 | 先提取微服务或模块边界。然后在边界内重构。“除了过多重构之外,一切都有对应的重构手法。” |
| IDE支持自动重构 | 使用它。现代IDE可安全地重命名、提取方法、引入变量等。仅当IDE无法处理时手动操作。 |
| 需要回退 | 使用 |
Resources
参考资源
- Martin Fowler, Refactoring: Improving the Design of Existing Code (2nd Edition, 2018)
- refactoring.com — Fowler's online catalog
- refactoring.guru — Illustrated refactoring patterns
- Martin Fowler, Refactoring: Improving the Design of Existing Code (第二版, 2018)
- refactoring.com — Fowler的在线目录
- refactoring.guru — 带插图的重构模式