| name | refactoring-techniques |
| description | Catalog of refactoring techniques from refactoring.guru - how to apply Extract Method, Move Field, Replace Conditional, and 60+ other refactorings |
Refactoring Techniques Catalog
Comprehensive reference of refactoring techniques for transforming code while preserving behavior. Based on Refactoring.Guru Techniques.
This skill is a **reference catalog** for applying refactorings. It is used by:
- `planning-refactor` when implementing improvements
- `code-smells` to suggest fixes for detected smells
- Standalone refactoring assistance
Cross-Reference: See code-smells skill for detecting WHAT to fix. This skill shows HOW to fix it.
When to Use
| Context | Trigger |
|---|
| Planning Refactor | Need specific technique to fix identified smell |
| Code Review | Suggesting improvements during PR review |
| Learning | User asks how to apply a specific refactoring |
| Quick Fix | User wants to apply a refactoring immediately |
Quick Reference: Smell to Refactoring
| Code Smell | Recommended Refactorings |
|---|
| Long Method | Extract Method, Replace Temp with Query, Decompose Conditional |
| Large Class | Extract Class, Extract Subclass, Extract Interface |
| Long Parameter List | Introduce Parameter Object, Preserve Whole Object |
| Duplicate Code | Extract Method, Pull Up Method, Form Template Method |
| Feature Envy | Move Method, Extract Method |
| Switch Statements | Replace Conditional with Polymorphism, Replace Type Code with Subclasses |
| Data Clumps | Extract Class, Introduce Parameter Object |
| Primitive Obsession | Replace Data Value with Object, Replace Type Code with Class |
| Shotgun Surgery | Move Method, Move Field, Inline Class |
| Divergent Change | Extract Class |
| Message Chains | Hide Delegate |
| Middle Man | Remove Middle Man |
| Refused Bequest | Replace Inheritance with Delegation |
Category 1: Composing Methods
Techniques for correctly structuring methods. Long methods are often the root of many problems.
Extract Method
| Aspect | Details |
|---|
| Impact | High - Most frequently used refactoring |
| Risk | Low |
| Fixes | Long Method, Duplicate Code, Comments |
Problem: Code fragment that can be logically grouped together.
Solution: Create a new method with a descriptive name and move the fragment there.
Steps:
- Create new method named after WHAT it does (not HOW)
- Copy the code fragment to the new method
- Look for local variables used only within the fragment - make them local to new method
- Pass external variables as parameters
- If extracted code modifies a variable needed later, return it
- Replace original fragment with method call
C++ Example:
void print_invoice(const Invoice& inv) {
std::cout << "================\n";
std::cout << "INVOICE #" << inv.number << "\n";
std::cout << "Date: " << inv.date << "\n";
std::cout << "================\n";
for (const auto& item : inv.items) {
std::cout << item.name << ": $" << item.price << "\n";
}
double total = 0;
for (const auto& item : inv.items) {
total += item.price;
}
std::cout << "Total: $" << total << "\n";
}
void print_header(const Invoice& inv) {
std::cout << "================\n";
std::cout << "INVOICE #" << inv.number << "\n";
std::cout << "Date: " << inv.date << "\n";
std::cout << "================\n";
}
void print_items(const std::vector<Item>& items) {
for (const auto& item : items) {
std::cout << item.name << ": $" << item.price << "\n";
}
}
double calculate_total(const std::vector<Item>& items) {
return std::accumulate(items.begin(), items.end(), 0.0,
[](double sum, const Item& item) { return sum + item.price; });
}
void print_invoice(const Invoice& inv) {
print_header(inv);
print_items(inv.items);
std::cout << "Total: $" << calculate_total(inv.items) << "\n";
}
Python Example:
def print_invoice(inv):
print("================")
print(f"INVOICE #{inv.number}")
print(f"Date: {inv.date}")
print("================")
total = 0
for item in inv.items:
print(f"{item.name}: ${item.price}")
total += item.price
print(f"Total: ${total}")
def print_header(inv):
print("================")
print(f"INVOICE #{inv.number}")
print(f"Date: {inv.date}")
print("================")
def print_items(items):
for item in items:
print(f"{item.name}: ${item.price}")
def calculate_total(items):
return sum(item.price for item in items)
def print_invoice(inv):
print_header(inv)
print_items(inv.items)
print(f"Total: ${calculate_total(inv.items)}")
Inline Method
| Aspect | Details |
|---|
| Impact | Low |
| Risk | Low |
| Fixes | Speculative Generality, excessive delegation |
Problem: Method body is more obvious than the method itself.
Solution: Replace method calls with the method content and delete the method.
Steps:
- Verify method is not overridden in subclasses
- Find all calls to the method
- Replace each call with the method body
- Delete the method
int get_rating() {
return more_than_five_late_deliveries() ? 2 : 1;
}
bool more_than_five_late_deliveries() {
return late_deliveries_ > 5;
}
int get_rating() {
return late_deliveries_ > 5 ? 2 : 1;
}
Extract Variable
| Aspect | Details |
|---|
| Impact | Medium |
| Risk | Low |
| Fixes | Complex expressions, Comments |
Problem: Hard-to-understand expression.
Solution: Place the expression result in a self-explanatory variable.
Steps:
- Insert a new variable and assign the expression to it
- Replace the original expression with the variable
- Repeat for other occurrences of the same expression
if (platform.find("MAC") != std::string::npos &&
browser.find("IE") != std::string::npos &&
was_initialized() && resize > 0) {
}
bool is_mac = platform.find("MAC") != std::string::npos;
bool is_ie = browser.find("IE") != std::string::npos;
bool was_resized = resize > 0;
if (is_mac && is_ie && was_initialized() && was_resized) {
}
Replace Temp with Query
| Aspect | Details |
|---|
| Impact | Medium |
| Risk | Low |
| Fixes | Long Method (preparation for Extract Method) |
Problem: Temporary variable storing an expression result for later use.
Solution: Move the expression to a new method and call the method instead of using the variable.
Steps:
- Ensure the variable is only assigned once
- Extract the expression into a new method
- Replace all uses of the variable with method calls
- Remove the variable declaration
double calculate_total() {
double base_price = quantity_ * item_price_;
if (base_price > 1000) {
return base_price * 0.95;
}
return base_price * 0.98;
}
double base_price() const {
return quantity_ * item_price_;
}
double calculate_total() {
if (base_price() > 1000) {
return base_price() * 0.95;
}
return base_price() * 0.98;
}
Split Temporary Variable
| Aspect | Details |
|---|
| Impact | Medium |
| Risk | Low |
| Fixes | Variable reuse, unclear intent |
Problem: Local variable used for multiple unrelated purposes.
Solution: Create separate variables for each purpose.
double temp = 2 * (height_ + width_);
std::cout << "Perimeter: " << temp << "\n";
temp = height_ * width_;
std::cout << "Area: " << temp << "\n";
double perimeter = 2 * (height_ + width_);
std::cout << "Perimeter: " << perimeter << "\n";
double area = height_ * width_;
std::cout << "Area: " << area << "\n";
Remove Assignments to Parameters
| Aspect | Details |
|---|
| Impact | Medium |
| Risk | Low |
| Fixes | Confusing parameter modification |
Problem: Value assigned to a parameter inside method body.
Solution: Use a local variable instead of the parameter.
int discount(int input_val, int quantity) {
if (quantity > 50) {
input_val -= 2;
}
return input_val;
}
int discount(int input_val, int quantity) {
int result = input_val;
if (quantity > 50) {
result -= 2;
}
return result;
}
Replace Method with Method Object
| Aspect | Details |
|---|
| Impact | High |
| Risk | Medium |
| Fixes | Long Method with intertwined local variables |
Problem: Long method where local variables prevent extraction.
Solution: Transform the method into a separate class where locals become fields.
Steps:
- Create a new class named after the method
- Create a private field for the original object and each local variable/parameter
- Create a constructor that initializes all fields
- Copy the method body to a
compute() method in the new class
- Replace original method with creation of method object and call to
compute()
class Order {