refactoring-to-patterns

Apply Fowler's refactoring patterns - recognize code smells, apply proven transformations

$ 安裝

git clone https://github.com/bostonaholic/dotfiles /tmp/dotfiles && cp -r /tmp/dotfiles/claude/skills/refactoring-to-patterns ~/.claude/skills/dotfiles

// tip: Run this command in your terminal to install the skill


name: refactoring-to-patterns description: Apply Fowler's refactoring patterns - recognize code smells, apply proven transformations

Refactoring to Patterns

Core Principle

Patterns solve recurring design problems. Know the patterns, recognize the smells, apply the right transformation.

MANDATORY: Always name the pattern explicitly. "Applying Compose Method" not "extracting some methods."

Pattern Quick Reference

Code SmellPatternIndicator
Long method, multiple responsibilitiesCompose MethodCan't describe in one sentence
Switch/if on object typeReplace Conditional with PolymorphismEach case handles different type
Duplicate algorithm structureForm Template MethodSame steps, different details
Scattered null checksIntroduce Null Objectif obj != null appears 3+ times
Type field drives behaviorReplace Type Code with State/StrategyBehavior varies by type field
Stacked conditional featuresMove Embellishment to DecoratorOptional features combined

Patterns

Compose Method

Break long method into small, well-named methods at same abstraction level.

# Before: Mixed abstraction levels
def process_order(order)
  raise "Empty" if order.items.empty?
  total = order.items.sum { |i| i.price * i.quantity }
  send_email(order.customer.email, "Confirmation", "...")
end

# After: Single abstraction level
def process_order(order)
  validate_order(order)
  total = calculate_total(order)
  send_confirmation(order, total)
end

Replace Conditional with Polymorphism

# Before: Type-based switch
def process_payment(payment)
  case payment.type
  when 'credit' then charge_credit(payment)
  when 'paypal' then charge_paypal(payment)
  end
end

# After: Polymorphic
class CreditPayment
  def process; charge_credit(self); end
end

def process_payment(payment)
  payment.process
end

Form Template Method

# Before: Duplicate structure
class PdfReport
  def generate(data)
    header + "<PDF>#{data}</PDF>" + footer
  end
end

# After: Template in base class
class Report
  def generate(data)
    header + generate_body(data) + footer
  end
end

class PdfReport < Report
  def generate_body(data); "<PDF>#{data}</PDF>"; end
end

Introduce Null Object

# Before: Scattered null checks
discount = customer ? customer.discount : 0
name = customer ? customer.name : "Guest"

# After: Null object
class NullCustomer
  def discount; 0; end
  def name; "Guest"; end
end

def find_customer(id)
  database.find(id) || NullCustomer.new
end

customer = find_customer(id)
discount = customer.discount  # No null check

Replace Type Code with State/Strategy

# Before: Type field with switches
class Employee
  def bonus(base)
    case @type
    when 'engineer' then base * 1.5
    when 'manager' then base * 2.0
    end
  end
end

# After: Delegated to type object
class Engineer
  def bonus(base); base * 1.5; end
end

class Employee
  def bonus(base); @type.bonus(base); end
end

When to Apply

Apply when:

  • Smell is clear and recurring
  • Pattern simplifies the code
  • Team understands the pattern
  • Extension point needed now (not "someday")

Avoid when:

  • Code already simple
  • Pattern adds more complexity than it removes
  • Guessing at future requirements (three strikes rule)

Recognition Workflow

  1. Identify smell → Map to pattern
  2. Confirm fit → Will it reduce complexity?
  3. Apply incrementally → Small steps, tests between
  4. Verify improvement → Easier to read/extend?

Key Takeaways

  1. Name patterns explicitly - Creates shared vocabulary
  2. Three strikes rule - Don't abstract until 3+ repetitions
  3. Test pattern fits - Will it simplify or complicate?
  4. Extract incrementally - Small steps with tests
  5. Same abstraction level - Methods in composed method should match