Refactoring Patterns
Comprehensive refactoring guide based on Martin Fowler's "Refactoring: Improving the Design of Existing Code."
Core Principle
Small Change → Test → Commit → Repeat
Every refactoring must preserve existing behavior while improving code structure.
The Refactoring Cycle
- •
Identify One Small, Isolated Change
- •Extract a single method/function
- •Rename one variable for clarity
- •Eliminate one piece of duplication
- •Simplify one conditional
- •ONE change at a time (< 10 lines affected)
- •
Make the Change
- •Preserve existing behavior (no new features)
- •Keep scope minimal
- •
Run All Tests Immediately
- •✅ Green? → Proceed to step 4
- •❌ Red? → Revert immediately, try smaller change
- •
Commit the Change
- •Descriptive message: "Refactor: extract calculateDiscount method"
- •Creates safety checkpoint for rollback
- •
Repeat - Build on the previous improvement
Safety Rules (Non-Negotiable)
- •NEVER refactor without passing tests
- •NEVER combine refactoring with new features
- •NEVER make "just one more small change" without testing
- •ALWAYS commit after each successful step
- •ALWAYS have a working state to revert to
- •Maximum 15 minutes per refactoring step (break down if longer)
Red-Green-Refactor Discipline
RED → Write failing test for new feature
GREEN → Minimal code to pass test (can be ugly)
REFACTOR → Improve code in tiny increments (test after each)
→ ONLY when all tests are green
→ Commit after each improvement
Refactoring Catalog
Composing Methods
Extract Method - Create well-named method from code fragment
- •Use when: Semantically dense code, different abstraction levels, replacing comments
- •Don't use if: Cannot give a better name than current method
- •Steps: Copy block → create method → test → replace with call → test → rename → test
Inline Method - Replace method call with method body
- •Use when: Needless indirection, preparing for larger refactoring
- •Don't use if: Subclass overrides the method
Inline Temp - Replace temp variable with direct method call
- •Use when: Temp assigned from method call only once
- •Often leads to: Replace Temp with Query
Replace Temp with Query - Extract expression to method, remove temp
- •Use when: Expression used once, temp assigned once
- •Benefit: Reduces method size, enables reuse
Introduce Explaining Variable - Put complex expression in temp with clear name
- •Use when: Complex conditionals need clarity
- •Alternative: Prefer Extract Method for recurring checks
Split Temporary Variable - Create separate temp for each assignment
- •Use when: Temp reused for different purposes (not loop/accumulator)
- •Identifies: Variables named temp, foo, x (poor names)
Remove Assignments to Parameters - Use temp instead of assigning to parameter
- •Avoids: Confusion between pass-by-value and pass-by-reference
- •Exception: Output parameters (use sparingly)
Replace Method with Method Object - Turn method into its own class
- •Introduces: Command Pattern
- •Use when: Long method with local variables preventing Extract Method
Substitute Algorithm - Replace algorithm with clearer/simpler one
- •Select based on: Performance, succinctness, expressiveness
- •Must have tests before substituting
Moving Features Between Objects
Move Method - Move method to class that uses it most
- •Use when: Method uses more features of another class
Move Field - Move field to class that uses it most
Extract Class - Create new class for subset of responsibilities
- •Use when: Class doing work of two classes
Inline Class - Merge class into another when it's no longer pulling weight
Hide Delegate - Create method to hide delegation
- •Encapsulates: Knowledge of other objects
Remove Middle Man - Call delegate directly when middle man is thin wrapper
Organizing Data
Replace Magic Number with Symbolic Constant
- •Turn literal numbers into named constants
Encapsulate Field - Make field private, provide accessors
Encapsulate Collection - Return read-only view, provide add/remove methods
Replace Type Code with Class - Replace integer codes with class
- •Alternative: Use enums or type-safe enums
Replace Type Code with Subclasses - Turn codes into inheritance
- •Use when: Behavior varies by type code
Replace Type Code with State/Strategy - Use State/Strategy pattern
- •Use when: Type code changes during lifetime, or can't use subclasses
Simplifying Conditional Expressions
Decompose Conditional - Extract condition and branches into methods
- •Example:
if (date.before(SUMMER_START))→if (notSummer(date))
Consolidate Conditional Expression - Combine conditions with same result
- •Use logical operators to unify checks
Consolidate Duplicate Conditional Fragments - Move identical code outside conditional
Remove Control Flag - Use break, continue, or return instead of flag variable
Replace Nested Conditional with Guard Clauses - Use early returns
- •Example:
if (!valid) return; // rest of method
Replace Conditional with Polymorphism - Move conditional to subclass methods
Introduce Null Object - Replace null checks with null object
- •Null object implements interface with do-nothing methods
Introduce Assertion - Make assumptions explicit
- •Example:
assert(balance >= 0, "Balance cannot be negative")
Making Method Calls Simpler
Rename Method - Make name explain purpose
Add Parameter / Remove Parameter - Adjust parameter list
Separate Query from Modifier - Split methods that return value and change state
Parameterize Method - Use parameter instead of multiple similar methods
Replace Parameter with Explicit Methods - Create separate method for each value
Preserve Whole Object - Pass entire object instead of multiple values
Replace Parameter with Method - Remove parameter by calling method
Introduce Parameter Object - Group parameters into object
Remove Setting Method - Make field immutable after construction
Hide Method - Make method private if not used outside class
Replace Constructor with Factory Method - Return polymorphic type
Replace Error Code with Exception - Throw exception instead of returning code
Replace Exception with Test - Check condition first to avoid exception
Dealing with Generalization
Pull Up Field / Pull Up Method - Move to superclass when identical in subclasses
Push Down Field / Push Down Method - Move to subclass when used only there
Extract Subclass - Create subclass for features used in some instances
Extract Superclass - Create superclass for common features
Extract Interface - Create interface when classes share subset of features
Collapse Hierarchy - Merge subclass into superclass when too similar
Form Template Method - Move invariant steps to superclass, override variants
Replace Inheritance with Delegation - Use composition when subclass uses only part of superclass
Replace Delegation with Inheritance - Make delegate a subclass if using all features
Example: Incremental Application
Bad Approach (risky - batched changes):
// Extract 3 methods + rename 5 variables + reorganize imports // Run tests → many failures → hard to debug
Good Approach (safe - incremental):
1. Extract calculateSubtotal() → test → commit 2. Extract calculateTax() → test → commit 3. Extract calculateTotal() → test → commit 4. Rename 'amt' to 'amount' → test → commit 5. Rename 'calc' to 'calculate' → test → commit
References
Based on Martin Fowler's "Refactoring: Improving the Design of Existing Code"