CrackedRuby CrackedRuby

Overview

The Template Method Pattern establishes an algorithm's invariant structure in a superclass while delegating certain steps to subclasses. This pattern defines a method that serves as a template, containing a sequence of method calls where some methods are abstract or have default implementations that subclasses can override.

The pattern originated in object-oriented design to address code duplication when multiple classes follow similar algorithmic structures but differ in specific implementation details. Instead of replicating the entire algorithm across classes, the Template Method Pattern extracts the common structure into a base class and isolates the varying parts into methods that subclasses override.

In Ruby, this pattern aligns naturally with the language's class-based inheritance model. The template method resides in a parent class and calls other methods (hook methods or primitive operations) that child classes implement or override. Ruby's dynamic nature and flexible method resolution make this pattern particularly expressive.

The pattern addresses several key scenarios: when multiple classes share algorithmic structure but differ in details, when code duplication across similar classes creates maintenance burden, or when an algorithm's invariant parts should be centralized while variant parts remain customizable.

# Basic structure
class DataProcessor
  def process
    load_data
    validate_data
    transform_data
    save_data
  end
  
  def load_data
    raise NotImplementedError
  end
  
  def validate_data
    # Default implementation
    true
  end
  
  def transform_data
    raise NotImplementedError
  end
  
  def save_data
    raise NotImplementedError
  end
end

Key Principles

The Template Method Pattern operates on several fundamental principles that define its behavior and structure.

Inversion of Control forms the pattern's core mechanism. The parent class controls the algorithm's flow, calling methods that subclasses implement. This inverts the typical control flow where child classes would call parent methods. The parent class dictates when and in what order operations occur, while subclasses provide the concrete implementations.

Hook methods represent optional extension points that subclasses may override but are not required to. These methods typically provide default behavior or do nothing. Hook methods differ from abstract methods in that they allow, but do not mandate, customization. A hook method might log information, perform optional validation, or provide default transformations that subclasses can replace.

Primitive operations are abstract methods that subclasses must implement. These represent the variable parts of the algorithm that differ across implementations. The template method calls primitive operations at specific points in the algorithm, relying on subclasses to provide concrete behavior.

Concrete operations provide default implementations that subclasses inherit but may override. These operations represent common behavior shared across most or all subclasses. They reduce code duplication by centralizing shared logic while remaining overridable for exceptional cases.

The relationship between these components creates a contract: the parent class defines the algorithm's structure and guarantees its execution order, while subclasses fulfill the contract by implementing required operations and optionally customizing hook methods.

Method visibility plays an important role in the pattern. Template methods are typically public, as they represent the primary interface clients use. Primitive operations and hook methods often have protected or private visibility to signal they are internal implementation details not meant for direct client invocation.

class ReportGenerator
  def generate
    prepare_data
    format_header
    format_body
    format_footer if include_footer?
    finalize
  end
  
  protected
  
  # Primitive operation - must implement
  def prepare_data
    raise NotImplementedError
  end
  
  # Primitive operation - must implement
  def format_body
    raise NotImplementedError
  end
  
  # Concrete operation - default implementation
  def format_header
    "Report generated at #{Time.now}\n\n"
  end
  
  # Hook method - optional override
  def format_footer
    "\n\nEnd of report"
  end
  
  # Hook method - optional override
  def include_footer?
    true
  end
  
  # Concrete operation
  def finalize
    # Common finalization logic
  end
end

Liskov Substitution Principle applies directly to this pattern. Any subclass implementing the template method should be substitutable for the parent class without breaking the algorithm's correctness. The parent class establishes preconditions and postconditions that subclasses must respect.

Open/Closed Principle manifests through the pattern's design. The algorithm structure remains closed for modification (defined in the parent class) but open for extension (through subclass implementations of primitive operations and hook methods).

The pattern distinguishes between the algorithm's abstract structure and its concrete implementations. The parent class encodes knowledge about when operations occur and in what sequence, while subclasses encode knowledge about what those operations do. This separation of concerns improves maintainability and testability.

Ruby Implementation

Ruby's class inheritance and method resolution provide natural support for the Template Method Pattern. The language's dynamic features and conventions make the pattern particularly ergonomic.

Basic implementation uses class inheritance with methods that subclasses override:

class DocumentConverter
  def convert(input_path, output_path)
    content = read_file(input_path)
    parsed = parse_content(content)
    transformed = transform(parsed)
    formatted = format_output(transformed)
    write_file(output_path, formatted)
  end
  
  protected
  
  def read_file(path)
    File.read(path)
  end
  
  def parse_content(content)
    raise NotImplementedError, "Subclasses must implement parse_content"
  end
  
  def transform(parsed_data)
    parsed_data
  end
  
  def format_output(data)
    raise NotImplementedError, "Subclasses must implement format_output"
  end
  
  def write_file(path, content)
    File.write(path, content)
  end
end

class MarkdownToHtmlConverter < DocumentConverter
  protected
  
  def parse_content(content)
    # Parse markdown
    content.split("\n").map { |line| parse_line(line) }
  end
  
  def transform(parsed_data)
    # Additional transformations
    parsed_data.map { |line| line.gsub(/\*\*(.+?)\*\*/, '<strong>\1</strong>') }
  end
  
  def format_output(data)
    "<html><body>#{data.join("\n")}</body></html>"
  end
  
  private
  
  def parse_line(line)
    return "<h1>#{line[2..-1]}</h1>" if line.start_with?("# ")
    return "<h2>#{line[3..-1]}</h2>" if line.start_with?("## ")
    "<p>#{line}</p>"
  end
end

converter = MarkdownToHtmlConverter.new
converter.convert("input.md", "output.html")

Handling abstract methods in Ruby differs from statically typed languages. Ruby has no built-in abstract method mechanism, so raising NotImplementedError serves as the convention:

class AbstractProcessor
  def process(data)
    validated = validate(data)
    return nil unless validated
    
    execute(validated)
  end
  
  protected
  
  def validate(data)
    # Default validation
    !data.nil? && !data.empty?
  end
  
  def execute(data)
    raise NotImplementedError, "#{self.class} must implement execute method"
  end
end

Hook methods with default behavior provide extension points without mandating overrides:

class BatchProcessor
  def process_batch(items)
    before_batch(items)
    
    results = items.map do |item|
      before_item(item)
      result = process_item(item)
      after_item(item, result)
      result
    end
    
    after_batch(results)
    results
  end
  
  protected
  
  def before_batch(items)
    # Hook - default does nothing
  end
  
  def before_item(item)
    # Hook - default does nothing
  end
  
  def process_item(item)
    raise NotImplementedError
  end
  
  def after_item(item, result)
    # Hook - default does nothing
  end
  
  def after_batch(results)
    # Hook - default does nothing
  end
end

class LoggingBatchProcessor < BatchProcessor
  protected
  
  def before_batch(items)
    puts "Processing #{items.size} items"
  end
  
  def process_item(item)
    item.upcase
  end
  
  def after_batch(results)
    puts "Completed processing #{results.size} items"
  end
end

Method visibility control uses Ruby's access modifiers to signal intent:

class Pipeline
  # Public interface - template method
  def execute(input)
    setup
    result = run_pipeline(input)
    cleanup
    result
  end
  
  protected
  
  # Protected - internal template structure
  def run_pipeline(input)
    stage1 = process_stage_one(input)
    stage2 = process_stage_two(stage1)
    process_stage_three(stage2)
  end
  
  # Protected - primitive operations
  def process_stage_one(input)
    raise NotImplementedError
  end
  
  def process_stage_two(input)
    raise NotImplementedError
  end
  
  def process_stage_three(input)
    raise NotImplementedError
  end
  
  private
  
  # Private - internal helpers not meant for override
  def setup
    # Setup logic
  end
  
  def cleanup
    # Cleanup logic
  end
end

Multiple template methods can coexist in the same class hierarchy:

class DataService
  def fetch_and_process(query)
    connection = connect
    raw_data = fetch(connection, query)
    process(raw_data)
  ensure
    disconnect(connection) if connection
  end
  
  def save_and_notify(data)
    validate_data(data)
    save(data)
    notify_subscribers(data) if should_notify?
  end
  
  protected
  
  def connect
    raise NotImplementedError
  end
  
  def fetch(connection, query)
    raise NotImplementedError
  end
  
  def process(data)
    data
  end
  
  def disconnect(connection)
    raise NotImplementedError
  end
  
  def validate_data(data)
    raise ArgumentError, "Invalid data" if data.nil?
  end
  
  def save(data)
    raise NotImplementedError
  end
  
  def notify_subscribers(data)
    # Default notification
  end
  
  def should_notify?
    true
  end
end

Super calls allow subclasses to extend rather than replace parent behavior:

class ImageProcessor
  def process(image)
    before_process
    result = apply_filters(image)
    after_process
    result
  end
  
  protected
  
  def before_process
    puts "Starting image processing"
  end
  
  def apply_filters(image)
    raise NotImplementedError
  end
  
  def after_process
    puts "Finished image processing"
  end
end

class ThumbnailGenerator < ImageProcessor
  protected
  
  def before_process
    super
    puts "Generating thumbnail"
  end
  
  def apply_filters(image)
    resize(image, 150, 150)
  end
  
  private
  
  def resize(image, width, height)
    # Resize implementation
    { data: image[:data], width: width, height: height }
  end
end

Practical Examples

The Template Method Pattern applies across various domains where algorithmic structure remains constant while implementation details vary.

Web scraper framework demonstrates the pattern with different parsing strategies:

class WebScraper
  def scrape(url)
    html = fetch_page(url)
    return nil unless html
    
    parsed = parse_html(html)
    extracted = extract_data(parsed)
    cleaned = clean_data(extracted)
    validate_and_return(cleaned)
  end
  
  protected
  
  def fetch_page(url)
    require 'net/http'
    uri = URI(url)
    response = Net::HTTP.get_response(uri)
    response.body if response.is_a?(Net::HTTPSuccess)
  end
  
  def parse_html(html)
    raise NotImplementedError
  end
  
  def extract_data(parsed)
    raise NotImplementedError
  end
  
  def clean_data(data)
    data.reject(&:empty?)
  end
  
  def validate_and_return(data)
    data.empty? ? nil : data
  end
end

class ProductScraper < WebScraper
  protected
  
  def parse_html(html)
    # Simplified parsing
    html.scan(/<div class="product">(.+?)<\/div>/m)
  end
  
  def extract_data(parsed)
    parsed.map do |product_html|
      {
        name: extract_product_name(product_html[0]),
        price: extract_price(product_html[0])
      }
    end
  end
  
  def clean_data(data)
    super.select { |item| item[:price] && item[:price] > 0 }
  end
  
  private
  
  def extract_product_name(html)
    html[/<h3>(.+?)<\/h3>/, 1]
  end
  
  def extract_price(html)
    price_str = html[/\$([0-9.]+)/, 1]
    price_str ? price_str.to_f : nil
  end
end

class ArticleScraper < WebScraper
  protected
  
  def parse_html(html)
    html.scan(/<article>(.+?)<\/article>/m)
  end
  
  def extract_data(parsed)
    parsed.map do |article_html|
      {
        title: article_html[0][/<h2>(.+?)<\/h2>/, 1],
        author: article_html[0][/<span class="author">(.+?)<\/span>/, 1],
        date: article_html[0][/<time>(.+?)<\/time>/, 1]
      }
    end
  end
end

Game AI decision-making shows the pattern with different strategy implementations:

class AIController
  def make_decision(game_state)
    analyze_situation(game_state)
    
    options = generate_options(game_state)
    scored_options = score_options(options, game_state)
    best_option = select_best(scored_options)
    
    execute_action(best_option, game_state)
  end
  
  protected
  
  def analyze_situation(game_state)
    # Hook method - optional analysis
  end
  
  def generate_options(game_state)
    raise NotImplementedError
  end
  
  def score_options(options, game_state)
    raise NotImplementedError
  end
  
  def select_best(scored_options)
    scored_options.max_by { |option| option[:score] }
  end
  
  def execute_action(option, game_state)
    option[:action].call(game_state)
  end
end

class AggressiveAI < AIController
  protected
  
  def generate_options(game_state)
    [
      { action: ->(_) { :attack }, type: :attack },
      { action: ->(_) { :defend }, type: :defend }
    ]
  end
  
  def score_options(options, game_state)
    options.map do |option|
      score = case option[:type]
              when :attack then calculate_attack_score(game_state)
              when :defend then calculate_defense_score(game_state)
              end
      option.merge(score: score)
    end
  end
  
  private
  
  def calculate_attack_score(game_state)
    game_state[:player_health] > 50 ? 100 : 60
  end
  
  def calculate_defense_score(game_state)
    game_state[:player_health] > 50 ? 30 : 70
  end
end

class DefensiveAI < AIController
  protected
  
  def analyze_situation(game_state)
    @threat_level = game_state[:enemy_nearby] ? :high : :low
  end
  
  def generate_options(game_state)
    [
      { action: ->(_) { :attack }, type: :attack },
      { action: ->(_) { :defend }, type: :defend },
      { action: ->(_) { :retreat }, type: :retreat }
    ]
  end
  
  def score_options(options, game_state)
    options.map do |option|
      base_score = base_scores[option[:type]]
      modifier = @threat_level == :high ? threat_modifiers[option[:type]] : 0
      option.merge(score: base_score + modifier)
    end
  end
  
  private
  
  def base_scores
    { attack: 40, defend: 70, retreat: 50 }
  end
  
  def threat_modifiers
    { attack: -20, defend: 30, retreat: 20 }
  end
end

Test execution framework illustrates the pattern with different test types:

class TestRunner
  def run_test(test_case)
    setup_test(test_case)
    
    begin
      prepare_environment
      result = execute_test(test_case)
      verify_result(result, test_case)
    rescue StandardError => e
      handle_failure(e, test_case)
    ensure
      cleanup_test(test_case)
    end
  end
  
  protected
  
  def setup_test(test_case)
    # Hook method
  end
  
  def prepare_environment
    # Common preparation
    @start_time = Time.now
  end
  
  def execute_test(test_case)
    raise NotImplementedError
  end
  
  def verify_result(result, test_case)
    raise NotImplementedError
  end
  
  def handle_failure(error, test_case)
    {
      status: :failed,
      error: error.message,
      test: test_case
    }
  end
  
  def cleanup_test(test_case)
    @end_time = Time.now
    log_execution_time
  end
  
  private
  
  def log_execution_time
    duration = @end_time - @start_time
    puts "Test completed in #{duration}s"
  end
end

class UnitTestRunner < TestRunner
  protected
  
  def setup_test(test_case)
    @mocks = []
    @stubs = []
  end
  
  def execute_test(test_case)
    test_case[:method].call
  end
  
  def verify_result(result, test_case)
    expected = test_case[:expected]
    {
      status: result == expected ? :passed : :failed,
      actual: result,
      expected: expected
    }
  end
  
  def cleanup_test(test_case)
    @mocks.each(&:reset)
    @stubs.each(&:reset)
    super
  end
end

class IntegrationTestRunner < TestRunner
  protected
  
  def setup_test(test_case)
    setup_database
    seed_test_data(test_case)
  end
  
  def execute_test(test_case)
    test_case[:scenario].call
  end
  
  def verify_result(result, test_case)
    checks = test_case[:assertions]
    failures = checks.reject { |check| check.call(result) }
    
    {
      status: failures.empty? ? :passed : :failed,
      failures: failures
    }
  end
  
  def cleanup_test(test_case)
    rollback_database
    super
  end
  
  private
  
  def setup_database
    # Database setup
  end
  
  def seed_test_data(test_case)
    # Insert test data
  end
  
  def rollback_database
    # Clean database
  end
end

Design Considerations

Selecting the Template Method Pattern requires evaluating several design factors and understanding when the pattern provides value versus when it introduces unnecessary complexity.

When to apply the pattern depends on specific code characteristics. Multiple classes implementing similar algorithms with shared structure but different details indicate a good candidate. Code duplication across classes where the duplication involves algorithmic flow rather than just individual methods suggests the pattern would centralize the common structure. Requirements for enforcing a specific sequence of operations across multiple implementations make the pattern valuable for ensuring consistency.

Inheritance depth affects the pattern's maintainability. Single-level inheritance (one abstract parent, multiple concrete children) remains straightforward and readable. Multi-level hierarchies where template methods call other template methods increase complexity and make the control flow harder to trace. Each additional inheritance level compounds the difficulty of understanding the complete algorithm execution path.

Flexibility trade-offs emerge between structure and customization. The pattern enforces a rigid algorithm structure, which provides consistency but limits flexibility. Subclasses cannot reorder operations or skip steps without violating the parent class's contract. When requirements demand variable operation ordering or conditional step execution, the pattern may be too restrictive.

Composition as alternative offers different trade-offs. The Strategy Pattern replaces inheritance with composition, allowing runtime algorithm selection and avoiding inheritance-related coupling. Composition provides more flexibility for combining behaviors but requires more upfront design and potentially more objects. The Template Method Pattern works better when the algorithm structure is truly invariant and inheritance relationships align with the domain model.

# Template Method - rigid structure
class ReportBuilder
  def build
    fetch_data
    format_data
    generate_output
  end
end

# Strategy - flexible composition
class ReportBuilder
  def initialize(data_fetcher, formatter, generator)
    @data_fetcher = data_fetcher
    @formatter = formatter
    @generator = generator
  end
  
  def build
    data = @data_fetcher.fetch
    formatted = @formatter.format(data)
    @generator.generate(formatted)
  end
end

Testability considerations differ between approaches. Template Method Pattern requires testing through inheritance, meaning tests instantiate concrete subclasses to verify behavior. This can make unit testing primitive operations in isolation more challenging. Composition-based alternatives allow testing each component independently by injecting test doubles.

Method granularity affects the pattern's usability. Too many small primitive operations fragment the algorithm and force subclasses to implement numerous methods. Too few large operations reduce flexibility and may require subclasses to duplicate logic. Finding the right balance requires understanding which variations actually occur in practice versus which are theoretical possibilities.

Domain alignment influences whether inheritance represents a natural relationship. The Template Method Pattern works best when the inheritance hierarchy reflects genuine "is-a" relationships in the domain. Forcing unrelated classes into an inheritance hierarchy solely to share algorithmic structure creates artificial coupling and violates domain semantics.

Hook method proliferation can clutter the interface. Each hook method adds a potential extension point, but too many hooks make the class harder to understand and use. Subclasses face decision fatigue determining which hooks to override. Including only hooks that address actual variation points rather than hypothetical future needs maintains clarity.

Change frequency impacts maintenance burden. When the algorithm structure changes frequently, modifying the parent class affects all subclasses. When individual implementations change frequently but the overall structure remains stable, the pattern isolates changes effectively. Assessing which aspect changes more helps determine if the pattern's benefits outweigh its constraints.

Common Patterns

Several established patterns and variations emerge when implementing Template Methods in practice.

Abstract class with NotImplementedError represents the standard Ruby approach for enforcing subclass implementation:

class AbstractService
  def perform
    prepare
    result = execute
    finalize(result)
    result
  end
  
  protected
  
  def prepare
    # Default preparation
  end
  
  def execute
    raise NotImplementedError, "#{self.class} must implement #execute"
  end
  
  def finalize(result)
    # Default finalization
  end
end

class ConcreteService < AbstractService
  protected
  
  def execute
    # Actual implementation
    { status: :success }
  end
end

Factory method integration combines template methods with object creation:

class DocumentProcessor
  def process
    parser = create_parser
    validator = create_validator
    
    parsed = parser.parse(input)
    return nil unless validator.valid?(parsed)
    
    transform(parsed)
  end
  
  protected
  
  def create_parser
    raise NotImplementedError
  end
  
  def create_validator
    raise NotImplementedError
  end
  
  def transform(data)
    raise NotImplementedError
  end
end

class JsonDocumentProcessor < DocumentProcessor
  protected
  
  def create_parser
    JsonParser.new
  end
  
  def create_validator
    JsonValidator.new(schema)
  end
  
  def transform(data)
    # JSON-specific transformation
  end
  
  private
  
  def schema
    # Return JSON schema
  end
end

Multi-phase template breaks complex algorithms into distinct phases:

class DataPipeline
  def run(input)
    # Phase 1: Acquisition
    raw_data = acquire_data(input)
    return nil unless raw_data
    
    # Phase 2: Transformation
    transformed = transform_phase(raw_data)
    return nil unless transformed
    
    # Phase 3: Output
    output_phase(transformed)
  end
  
  protected
  
  def acquire_data(input)
    load(input)
  end
  
  def transform_phase(data)
    validated = validate(data)
    return nil unless validated
    
    cleaned = clean(validated)
    enrich(cleaned)
  end
  
  def output_phase(data)
    formatted = format(data)
    deliver(formatted)
  end
  
  # Primitive operations
  def load(input)
    raise NotImplementedError
  end
  
  def validate(data)
    true
  end
  
  def clean(data)
    data
  end
  
  def enrich(data)
    raise NotImplementedError
  end
  
  def format(data)
    raise NotImplementedError
  end
  
  def deliver(formatted)
    raise NotImplementedError
  end
end

Conditional hook execution uses predicate methods to control hook invocation:

class CachedOperation
  def execute(key)
    cached = fetch_from_cache(key) if use_cache?
    return cached if cached
    
    result = perform_operation(key)
    
    store_in_cache(key, result) if use_cache? && cache_result?(result)
    
    result
  end
  
  protected
  
  def perform_operation(key)
    raise NotImplementedError
  end
  
  def use_cache?
    true
  end
  
  def cache_result?(result)
    true
  end
  
  def fetch_from_cache(key)
    # Cache fetch implementation
  end
  
  def store_in_cache(key, result)
    # Cache store implementation
  end
end

class ExpensiveOperation < CachedOperation
  protected
  
  def perform_operation(key)
    # Expensive computation
    sleep(2)
    "result for #{key}"
  end
  
  def cache_result?(result)
    !result.nil? && !result.empty?
  end
end

class RealTimeOperation < CachedOperation
  protected
  
  def perform_operation(key)
    # Time-sensitive operation
    Time.now.to_s
  end
  
  def use_cache?
    false
  end
end

Error handling template centralizes exception handling patterns:

class ResilientOperation
  def execute
    attempts = 0
    max_attempts = retry_limit
    
    begin
      attempts += 1
      before_attempt(attempts)
      result = perform
      after_successful_attempt(result)
      result
    rescue StandardError => e
      if should_retry?(e, attempts, max_attempts)
        wait_before_retry(attempts)
        retry
      else
        handle_final_failure(e, attempts)
      end
    end
  end
  
  protected
  
  def perform
    raise NotImplementedError
  end
  
  def retry_limit
    3
  end
  
  def should_retry?(error, attempts, max_attempts)
    attempts < max_attempts && retryable_error?(error)
  end
  
  def retryable_error?(error)
    true
  end
  
  def wait_before_retry(attempts)
    sleep(attempts * 0.5)
  end
  
  def before_attempt(attempts)
    # Hook
  end
  
  def after_successful_attempt(result)
    # Hook
  end
  
  def handle_final_failure(error, attempts)
    raise error
  end
end

State-carrying template maintains state across hook invocations:

class StatefulProcessor
  def process(items)
    @state = initialize_state
    
    items.each do |item|
      process_item(item)
      update_state(item)
    end
    
    finalize_state
  end
  
  protected
  
  attr_reader :state
  
  def initialize_state
    { processed: 0, errors: 0 }
  end
  
  def process_item(item)
    raise NotImplementedError
  end
  
  def update_state(item)
    @state[:processed] += 1
  end
  
  def finalize_state
    @state
  end
end

class ValidatingProcessor < StatefulProcessor
  protected
  
  def process_item(item)
    if valid?(item)
      transform(item)
    else
      handle_invalid(item)
    end
  end
  
  def update_state(item)
    super
    @state[:errors] += 1 unless valid?(item)
  end
  
  private
  
  def valid?(item)
    !item.nil? && item.respond_to?(:to_s)
  end
  
  def transform(item)
    item.to_s.upcase
  end
  
  def handle_invalid(item)
    # Error handling
  end
end

Common Pitfalls

Several mistakes commonly occur when implementing or using the Template Method Pattern.

Excessive abstraction happens when developers create template methods for algorithms that have insufficient variation across implementations. Creating an abstract base class with multiple primitive operations when only one or two subclasses exist or when the variations are minimal introduces unnecessary complexity without corresponding benefit. The pattern adds value when multiple concrete implementations share substantial algorithmic structure, not when variations are minor or implementations are few.

# Over-abstracted - only one subclass needed
class DataLoader
  def load
    connect
    fetch
    disconnect
  end
  
  protected
  
  def connect; raise NotImplementedError; end
  def fetch; raise NotImplementedError; end
  def disconnect; raise NotImplementedError; end
end

# Better - direct implementation
class FileLoader
  def load(path)
    File.read(path)
  end
end

Fragile base class problem arises when subclasses depend on parent class implementation details rather than just the contract. Changes to the parent class's internal methods or instance variables break subclasses that access or modify them. This coupling makes the hierarchy brittle and difficult to evolve.

# Fragile - subclass depends on parent internals
class BaseProcessor
  def process
    @data = load_data
    transform_internal
  end
  
  protected
  
  def transform_internal
    @data.map(&:upcase)
  end
end

class SubProcessor < BaseProcessor
  protected
  
  def transform_internal
    @data.map(&:downcase)  # Depends on @data existing
  end
end

# Better - explicit parameter passing
class BaseProcessor
  def process
    data = load_data
    transform(data)
  end
  
  protected
  
  def transform(data)
    data.map(&:upcase)
  end
end

Incorrect hook method defaults occur when default implementations make assumptions that don't hold for all subclasses. A hook method should either do nothing by default or perform truly universal operations. Defaults that work for some but not all subclasses force subclasses to override with empty methods, which signals a design problem.

Deep inheritance hierarchies reduce code comprehension by spreading algorithm implementation across multiple classes. Tracing execution requires jumping between parent and child classes repeatedly. Each inheritance level adds cognitive load and makes debugging more difficult. Limiting template method hierarchies to one or two levels maintains understandability.

Violated substitutability happens when subclasses override methods in ways that break the parent class's expectations. If the template method assumes certain preconditions or postconditions for primitive operations, subclasses must respect those assumptions. Violations lead to subtle bugs that surface only in specific execution scenarios.

# Violated contract
class BaseValidator
  def validate(data)
    # Assumes check_required returns boolean
    return false unless check_required(data)
    check_format(data)
  end
  
  protected
  
  def check_required(data)
    raise NotImplementedError
  end
  
  def check_format(data)
    true
  end
end

class BrokenValidator < BaseValidator
  protected
  
  def check_required(data)
    nil  # Returns nil instead of boolean - breaks template logic
  end
end

Missing NotImplementedError in Ruby makes the absence of required methods harder to detect. Without explicitly raising NotImplementedError, calling an unimplemented method results in NoMethodError at runtime, which provides less informative error messages and delays error detection.

Overriding template methods themselves rather than primitive operations breaks the pattern. Subclasses should override the steps (primitive operations) but not the template method that orchestrates them. Overriding the template method defeats the purpose of centralizing the algorithm structure.

State management confusion emerges when instance variables set in the template method are accessed or modified in primitive operations without clear documentation. Implicit state sharing between the template method and primitive operations creates hidden dependencies and makes behavior harder to predict.

# Confusing state management
class Processor
  def process
    @state = :initializing
    step_one
    @state = :processing
    step_two
    @state = :complete
  end
  
  protected
  
  def step_one
    # Unclear what @state should be here
  end
  
  def step_two
    # Subclass doesn't know it can access @state
  end
end

# Clearer - explicit parameters
class Processor
  def process
    context = { state: :initializing }
    step_one(context)
    context[:state] = :processing
    step_two(context)
  end
  
  protected
  
  def step_one(context)
    # Context is explicit parameter
  end
  
  def step_two(context)
    # Context is explicit parameter
  end
end

Insufficient documentation leaves subclass implementers uncertain about requirements. Each primitive operation needs documentation specifying parameters, return values, side effects, and any assumptions the template method makes about the operation's behavior. Without this information, implementers may create correct-looking but semantically incorrect implementations.

Reference

Pattern Structure

Component Description Characteristics
Abstract Class Defines template method and primitive operations Contains algorithm skeleton
Template Method Public method orchestrating algorithm steps Calls primitive operations in defined order
Primitive Operation Abstract method requiring subclass implementation Represents variable algorithm step
Hook Method Optional extension point with default implementation Allows but does not require override
Concrete Class Implements primitive operations Provides specific behavior

Method Types

Type Implementation Override Requirement Visibility
Template Method Concrete in parent Should not override Public
Primitive Operation Abstract in parent Must override Protected
Hook Method Default in parent May override Protected
Concrete Operation Concrete in parent May override Protected/Private

Implementation Checklist

Step Action Verification
Identify invariant structure Extract common algorithm flow across classes Algorithm steps same across implementations
Define abstract class Create parent class with template method Template method orchestrates algorithm
Identify primitive operations Determine varying steps needing implementation Each variation point has primitive operation
Add hook methods Create optional extension points Default behavior appropriate for most cases
Set method visibility Make template public, primitives protected Interface clear and protected from misuse
Document contracts Specify preconditions and postconditions Subclass implementers understand requirements
Implement concrete classes Override primitive operations and desired hooks Each subclass provides complete implementation

Design Decision Matrix

Consideration Template Method Strategy Pattern When to Prefer
Algorithm structure Fixed in parent Variable through composition Template Method: Structure truly invariant
Flexibility Limited to primitive operations High runtime flexibility Strategy: Need runtime algorithm swapping
Coupling Parent-child inheritance coupling Loose coupling via interfaces Strategy: Avoid inheritance coupling
Number of variations Best for 2-5 implementations Scales to many strategies Strategy: Many algorithm variants
Change frequency Structure changes affect all subclasses Easy to add new strategies Strategy: Frequent algorithm additions

Common Anti-Patterns

Anti-Pattern Problem Solution
Single subclass Unnecessary abstraction Implement directly without parent
Empty hook overrides Default implementation not universal Reconsider default or make primitive
Template override Subclass replaces template method Override only primitive operations
Deep inheritance Algorithm spread across multiple levels Flatten hierarchy, use composition
Implicit state Hidden instance variable dependencies Pass state explicitly as parameters
Missing contracts Unclear primitive operation requirements Document preconditions and postconditions

Ruby Idioms

Idiom Implementation Purpose
NotImplementedError raise NotImplementedError in abstract method Signal required implementation
Protected visibility protected keyword for primitive operations Internal API for subclasses
Super calls super in overridden method Extend parent behavior
Method presence check respond_to? before calling optional method Graceful handling of optional overrides
Documentation YARD comments on primitive operations Clarify contracts for implementers