You have a structural design problem that cannot be fixed in a single refactoring session. The individual moves — Extract Method, Move Method, Extract Class — are not in question. The challenge is that the problem is architectural: dozens of classes are tangled, or a hierarchy is doing multiple jobs, or procedural logic is spread across a codebase that nominally uses objects. The fix takes weeks to months, not minutes.
This skill is for those campaigns.
Fowler and Beck's core principle: "You refactor not because it is fun but because there are things you expect to be able to do with your programs if you refactor that you just can't do if you don't."
Big refactorings are done for a purpose — specifically because a particular kind of change that the team needs to make is blocked or costly without the restructuring. You are not refactoring for cleanliness. You are refactoring because the architecture is standing in the way of features you need to build.
This skill applies to four named patterns:
| Pattern | Core problem | Time scale |
|---|---|---|
| --------- | ------------- | ------------ |
| Tease Apart Inheritance | One hierarchy doing two independent jobs | Weeks |
| Convert Procedural Design to Objects | OO language used in procedural style | Weeks to months |
| Separate Domain from Presentation | Business logic embedded in GUI classes | Weeks |
| Extract Hierarchy | God class with accumulated conditionals | Weeks to months |
Prerequisites: Run build-refactoring-test-suite before starting. Big refactorings require a test suite that can catch regressions — the campaign moves incrementally and tests must confirm each step is safe.
Scan the codebase to identify which structural pattern is present:
Tease Apart Inheritance signals:
- Subclasses with identical prefix adjectives at every level
(TabularActiveDeal, TabularPassiveDeal — "Tabular" appears in both branches)
- Adding a new variation in one dimension requires adding subclasses in every branch
- Hierarchy depth exceeds 3 levels with cross-cutting concerns
Convert Procedural Design to Objects signals:
- Classes with only static methods or methods that take data objects as parameters
- Data classes (fields + accessors, no behavior) that are passed into procedure-style classes
- Long methods (50+ lines) on classes with few or no instance variables
- A single "calculator" or "processor" class that takes many different data objects
Separate Domain from Presentation signals:
- SQL statements inside window, panel, dialog, or view classes
- Window/form classes over 300 lines with business logic in event handlers
- Pricing, discount, or calculation logic in GUI event handlers
- java.sql imports in UI classes; database calls triggered directly by UI events
Extract Hierarchy signals:
- One class with 10+ boolean flags or type code fields
- Methods with large switch/case or if-elif chains that check the same flag
- Adding a new "type" or "mode" requires editing the same class in 5+ methods
- The class's behavior changes entirely based on a flag set at construction time
ACTION: Read the target code, identify which structural pattern applies, and confirm the diagnosis.
WHY: The four patterns require different remedies. Applying Extract Hierarchy mechanics to a tangled inheritance hierarchy will make it worse. Applying Tease Apart Inheritance to a god class with conditionals will create a hierarchy where subclassing is not the right solution. Correct pattern identification determines whether the campaign succeeds.
Work through the pattern-confirmation checklist:
Tease Apart Inheritance — confirm by answering:
Tabular appearing in both ActiveDeal and PassiveDeal branches)If yes to all three: this is Tease Apart Inheritance.
Convert Procedural Design to Objects — confirm by answering:
calculatePrice(Order order) on OrderCalculator)?If yes: this is Convert Procedural Design to Objects.
Separate Domain from Presentation — confirm by answering:
If yes: this is Separate Domain from Presentation.
Extract Hierarchy — confirm by answering:
If yes: this is Extract Hierarchy. Then determine the variant:
ACTION: Based on the confirmed pattern, select the execution strategy and identify the key decision points.
WHY: Each pattern has a fixed sequence of moves, but the sequence has decision points that cannot be fully specified in advance — the specific code you encounter will determine which moves apply. Knowing the decision points before starting prevents mid-campaign confusion and ensures the team pushes through when the refactoring gets messy.
Problem: One hierarchy is doing two jobs. (Example: Deal hierarchy also captures presentation style, creating TabularActiveDeal, TabularPassiveDeal.)
Goal: Two clean, focused hierarchies connected by delegation.
Sequence:
TabularPresentationStyle, SinglePresentationStyle). Initialize the instance variable in each original subclass to the appropriate subclass of the extracted class. Why: until the subclasses are created, the extracted class is just an empty shell with no behavior — this step populates the second hierarchy.Key decision point: Which job stays? Lean toward leaving the job with more code in place. If the code is evenly split, choose the job that is semantically primary — the job that gives the class its name.
Problem: An object-oriented language is being used in a procedural style. Behavior is concentrated in procedure classes; data is concentrated in data-holder classes with no behavior.
Goal: Behavior distributed into the data objects where it belongs.
Sequence:
Key decision point: What to do when a method uses data from multiple record types equally. Move Method to the record type it uses most; extract the portions that use other records into separate methods that can be moved to those records.
Problem: GUI classes contain domain logic. Business rules, SQL queries, and calculations are embedded in window or event handler classes.
Goal: Clean separation where GUI classes handle only presentation and domain classes carry all business logic.
Sequence:
java.sql (or equivalent), the separation is largely complete. Why: SQL in a window class is the clearest signal of domain logic in the wrong place; its removal confirms the domain class is carrying its responsibilities.Key decision point: What to do with data that is both displayed and used in domain logic. Always use Duplicate Observed Data as the intermediate step. Direct Move Field will break the GUI. Accept the duplication temporarily — it is easier to eliminate once the logic is clearly in the domain class.
Problem: A single class is doing too much, controlled by flags or type codes, with behavior that varies entirely based on those flags.
Prerequisite check: The conditional logic must be static during the object's lifetime. If the flags can change after construction, apply Extract Class first to create a separate object for the varying aspect before applying Extract Hierarchy. Why: Extract Hierarchy uses subclasses to represent variations. Subclass membership cannot change after instantiation — if a flag changes at runtime, the variation cannot be a subclass.
Variant A — Unclear variations (discover one at a time):
Variant B — Clear variations (create all subclasses at once):
Key decision point: Which variant to use. Use Variant A when you are unsure how many variations exist or the existing conditional logic is inconsistent. Use Variant B when the full set of variations is well-understood and each variation is consistently represented in the conditional logic.
ACTION: Produce a phased campaign plan the team can execute over weeks or months.
WHY: Big refactorings that are not planned as campaigns get abandoned. The team hits a messy intermediate state, feature pressure mounts, and the refactoring is frozen half-done — a state that is worse than the starting point. A campaign plan with explicit milestones and interleaving rules allows the team to make progress every day without disrupting feature delivery.
Campaign plan structure:
# [Pattern Name] Campaign Plan — [Target Class/System]
## Purpose
[The specific feature or change that is currently blocked. Why this refactoring now.]
## Pattern
[Selected pattern and variant, with one-sentence rationale for the selection]
## Team Alignment Required
Big refactorings require shared awareness. Every developer working in the affected
area must know:
1. This refactoring is "in play"
2. Which classes are being restructured
3. How to continue the refactoring when adding new code (new code goes into the
new structure, not the old one)
[List the classes and files "in play" for this campaign]
## Milestones
### Milestone 1: [Name] — [Estimated: N days]
Goal: [What structural state the code is in at the end of this milestone]
Steps:
1. [Specific move to apply]
2. [Specific move to apply]
...
Done when: [Specific, observable condition — e.g., "TabularPresentationStyle class exists
and holds all tabular layout methods"]
### Milestone 2: [Name] — [Estimated: N days]
[...]
## Interleaving Rules
- Refactoring steps happen during feature work, not in a dedicated freeze.
- Each milestone step is small enough to complete between feature commits.
- New features that touch the affected area use the new structure, not the old.
- Do not skip milestones to reach the end state — intermediate states must compile
and pass all tests.
## Decision Points
[List the key decisions that cannot be made until the code is read at that milestone:
e.g., "At Milestone 2: decide which job stays in the original hierarchy based on
line count of each dimension"]
## Stopping Condition
[The specific observable state that means the refactoring is done enough:
e.g., "When OrderWindow no longer imports java.sql"]
[The specific feature or change that was blocked must now be easy to make]
ACTION: Establish the rules for how the campaign runs alongside feature development.
WHY: "Refactoring not because it is fun but because there are things you expect to do with your programs" — Fowler and Beck. Big refactorings must coexist with feature delivery. The alternative — a two-month code freeze to refactor — is not available in production systems. The nibble-at-the-edges approach is the only viable one.
Interleaving rules (apply to every big refactoring campaign):
ACTION: Establish when the campaign is done and communicate it to the team.
WHY: Big refactorings without a stopping condition run forever or collapse under pressure. The stopping condition is the purpose stated in terms of observable code state. It is the answer to "how will we know when we are done?"
Stopping condition by pattern:
1. Big refactorings are purposeful, not cosmetic.
The motivation for a big refactoring is always a specific blocked capability. "The code is messy" is not sufficient motivation — a campaign that long will be abandoned. "We cannot add a new billing scheme without editing conditional logic in twelve methods of BillingScheme" is a sufficient motivation. The purpose drives the campaign and defines when it is complete.
2. Steps cannot be fully specified in advance.
Unlike the individual refactoring moves in Chapters 6-11, the steps of a big refactoring emerge as you do it. The pattern provides the strategy; the code provides the specifics. The plan must include decision points — places where you explicitly pause to assess the code and decide the next move based on what you find.
3. Team agreement is mandatory.
A big refactoring affects every developer working in the area. If one developer is teasing apart a hierarchy while another is subclassing the old tangled one, the campaign moves backward. Every developer must know that the refactoring is "in play" and must use the new structure when writing new code.
4. Intermediate states are dangerous — tests must pass at every step.
The period between starting and finishing a big refactoring is the most dangerous time. The code is in a partially restructured state. Without a test suite that passes after each step, the team cannot detect regressions. Run build-refactoring-test-suite before starting any campaign.
5. Accumulation is the enemy.
Fowler and Beck: "Accumulation of half-understood design decisions eventually chokes a program as a water weed chokes a canal." Big refactorings address accumulated design debt. They are not optional maintenance — they are necessary for the system to remain changeable.
Situation: A codebase has a Deal hierarchy with subclasses ActiveDeal, PassiveDeal, TabularActiveDeal, and TabularPassiveDeal. Every time a new deal type is added, tabular and non-tabular subclasses must both be added. Every time a new presentation style is added, active and passive subclasses must both be added. The team needs to add a chart-based presentation style.
Pattern confirmed: Tease Apart Inheritance — same adjective prefix ("Tabular") appears in both branches; the hierarchy is growing combinatorially.
Two-dimensional grid:
| Active Deal | Passive Deal |
-------------|-------------|--------------|
(single) | X | X |
Tabular | X | X |
Decision — which job stays: Deal type (Active/Passive) has more code; presentation style (single/tabular) has less. Extract presentation style.
Campaign milestones:
Milestone 1 (Day 1-2): Apply Extract Class at Deal superclass — create PresentationStyle. Add presentation instance variable to Deal. Tests pass.
Milestone 2 (Day 3-5): Create TabularPresentationStyle and SinglePresentationStyle subclasses of PresentationStyle. Initialize presentation in TabularActiveDeal and TabularPassiveDeal constructors to TabularPresentationStyle. Tests pass.
Milestone 3 (Day 6-10): Apply Move Method in each tabular subclass — move presentation-related methods to TabularPresentationStyle. When a subclass is empty, delete it. Apply Move Method in single subclasses similarly. Tests pass after each deletion.
Milestone 4 (Day 11-12): Apply Pull Up Method/Field to simplify each hierarchy now that they are separated. Tests pass.
Stopping condition: TabularActiveDeal and TabularPassiveDeal are deleted. Adding ChartPresentationStyle requires only one new class.
Situation: An OrderWindow class (400 lines) handles all GUI rendering and also contains SQL queries for fetching product prices, customer discount calculations, and order total computation. The team cannot unit test pricing logic because it is embedded in GUI event handlers.
Pattern confirmed: Separate Domain from Presentation — SQL statements in a window class, business calculation in event handlers.
Purpose: Make pricing logic unit-testable without a running GUI.
Campaign milestones:
Milestone 1 (Day 1): Create Order domain class linked from OrderWindow. Create OrderLine for grid rows. Tests pass (no behavior moved yet).
Milestone 2 (Day 2-5): Examine each field. customerCodes field is not displayed — apply Move Field directly to Order. Display fields (customerName, amount) — apply Duplicate Observed Data: add mirrored fields to Order with sync. Tests pass.
Milestone 3 (Day 6-15): Apply Extract Method to isolate SQL calls and pricing calculations in OrderWindow. Apply Move Method to transfer each extracted operation to Order. Drive all SQL toward Order. Tests pass after each move.
Observable stopping signal: OrderWindow no longer imports java.sql. Pricing methods exist on Order and can be unit tested without instantiating the GUI.
Situation: BillingScheme has flags for disability status, lifeline status, and business type. Every method contains conditional logic checking these flags. Adding a new billing category requires editing conditional logic in eight methods.
Pattern confirmed: Extract Hierarchy, Variant A — variations not fully clear upfront; flags static during object lifetime.
First variation picked: disability scheme.
Campaign milestones:
Milestone 1: Apply Replace Constructor with Factory Method on BillingScheme. Create DisabilityBillingScheme subclass. Factory method returns DisabilityBillingScheme when disability flag is set. Tests pass.
Milestone 2: Copy createBill to DisabilityBillingScheme. Simplify — remove branches that check disabilityScheme() since the subclass is always in disability context. Tests pass.
Milestone 3: Continue for other methods with disability conditionals. Pick next variation (lifeline). Create LifelineBillingScheme. Repeat. Tests pass after each subclass iteration.
Milestone 4: When all variations are subclassed, declare BillingScheme abstract. Delete conditional method bodies from the superclass. Tests pass.
Stopping condition: Adding a new billing category requires creating one new subclass of BillingScheme — no edits to existing classes.
| File | Contents | When to read |
|---|---|---|
| ------ | ---------- | -------------- |
references/big-refactoring-patterns.md | Full mechanics for all four patterns with decision tables and edge cases | Step 2 — selecting execution strategy |
Related skills:
code-smell-diagnosis — run first to identify which pattern applies (Parallel Inheritance Hierarchies, Data Class, or Large Class god class)build-refactoring-test-suite — run before starting any campaign to establish the safety netmethod-decomposition-refactoring — for the Extract Method and Move Method steps within the campaignconditional-simplification-strategy — for the Replace Conditional with Polymorphism steps in Extract HierarchyThis skill is licensed under CC-BY-SA-4.0.
Source: BookForge — Refactoring: Improving the Design of Existing Code by Martin Fowler and Kent Beck.
Install related skills from ClawhHub:
clawhub install bookforge-code-smell-diagnosisclawhub install bookforge-build-refactoring-test-suiteclawhub install bookforge-method-decomposition-refactoringclawhub install bookforge-conditional-simplification-strategyOr install the full book set from GitHub: bookforge-skills
共 1 个版本
暂无安全检测报告