- 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](https://refactoring.guru/refactoring/techniques).
<IMPORTANT>
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.
</IMPORTANT>
## 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:**
1. Create new method named after WHAT it does (not HOW)
2. Copy the code fragment to the new method
3. Look for local variables used only within the fragment - make them local to new method
4. Pass external variables as parameters
5. If extracted code modifies a variable needed later, return it
6. Replace original fragment with method call
**C++ Example:**
```cpp
// BEFORE
void print_invoice(const Invoice& inv) {
// Print header
std::cout << "================\n";
std::cout << "INVOICE #" << inv.number << "\n";
std::cout << "Date: " << inv.date << "\n";
std::cout << "================\n";
// Print items
for (const auto& item : inv.items) {
std::cout << item.name << ": $" << item.price << "\n";
}
// Calculate and print total
double total = 0;
for (const auto& item : inv.items) {
total += item.price;
}
std::cout << "Total: $" << total << "\n";
}
// AFTER
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:**
```python
# BEFORE
def print_invoice(inv):
# Print header
print("================")
print(f"INVOICE #{inv.number}")
print(f"Date: {inv.date}")
print("================")
# Print items and calculate total
total = 0
for item in inv.items:
print(f"{item.name}: ${item.price}")
total += item.price
print(f"Total: ${total}")
# AFTER
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:**
1. Verify method is not overridden in subclasses
2. Find all calls to the method
3. Replace each call with the method body
4. Delete the method
```cpp
// BEFORE
int get_rating() {
return more_than_five_late_deliveries() ? 2 : 1;
}
bool more_than_five_late_deliveries() {
return late_deliveries_ > 5;
}
// AFTER
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:**
1. Insert a new variable and assign the expression to it
2. Replace the original expression with the variable
3. Repeat for other occurrences of the same expression
```cpp
// BEFORE
if (platform.find("MAC") != std::string::npos &&
browser.find("IE") != std::string::npos &&
was_initialized() && resize > 0) {
// ...
}
// AFTER
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:**
1. Ensure the variable is only assigned once
2. Extract the expression into a new method
3. Replace all uses of the variable with method calls
4. Remove the variable declaration
```cpp
// BEFORE
double calculate_total() {
double base_price = quantity_ * item_price_;
if (base_price > 1000) {
return base_price * 0.95;
}
return base_price * 0.98;
}
// AFTER
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.
```cpp
// BEFORE
double temp = 2 * (height_ + width_);
std::cout << "Perimeter: " << temp << "\n";
temp = height_ * width_;
std::cout << "Area: " << temp << "\n";
// AFTER
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.
```cpp
// BEFORE
int discount(int input_val, int quantity) {
if (quantity > 50) {
input_val -= 2; // Modifying parameter!
}
return input_val;
}
// AFTER
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:**
1. Create a new class named after the method
2. Create a private field for the original object and each local variable/parameter
3. Create a constructor that initializes all fields
4. Copy the method body to a `compute()` method in the new class
5. Replace original method with creation of method object and call to `compute()`
```cpp
// BEFORE
class Order {
Ver en GitHub