Template Method Pattern: Define Algorithm Skeleton

Updated 2026-03-11

TL;DR

The Template Method Pattern defines the skeleton of an algorithm in a base class, deferring specific steps to subclasses. Subclasses can override certain steps without changing the algorithm’s overall structure. This pattern promotes code reuse and enforces a consistent process across variations.

Prerequisites: Understanding of inheritance and polymorphism, familiarity with abstract classes and methods, basic knowledge of method overriding in object-oriented programming.

After this topic: Implement the Template Method Pattern to create reusable algorithm skeletons, identify when this pattern solves code duplication problems, and design class hierarchies that enforce consistent workflows while allowing customization of specific steps.

Core Concept

What is the Template Method Pattern?

The Template Method Pattern is a behavioral design pattern that defines the structure of an algorithm in a parent class but lets subclasses override specific steps without changing the algorithm’s flow. The parent class contains a template method that calls a series of steps, some of which are concrete (implemented in the parent) and others are abstract (must be implemented by subclasses).

Why It Matters

This pattern solves a common problem: you have multiple classes that perform similar operations with slight variations. Without this pattern, you’d duplicate the common logic across classes, violating the DRY (Don’t Repeat Yourself) principle. The Template Method Pattern extracts the common structure into a base class, making your code more maintainable and consistent.

Key Components

Abstract Class: Contains the template method and defines the algorithm’s skeleton. It may include:

  • Template method (usually final/non-overridable): Orchestrates the algorithm steps
  • Abstract methods: Steps that subclasses must implement
  • Hook methods: Optional steps with default (often empty) implementations that subclasses can override
  • Concrete methods: Common steps implemented in the base class

Concrete Classes: Inherit from the abstract class and provide specific implementations for abstract steps.

How It Works

The template method calls a sequence of methods in a specific order. Some methods are already implemented (shared behavior), while others are placeholders that subclasses fill in (variable behavior). This inverts the typical control flow — instead of subclasses calling parent methods, the parent’s template method calls subclass methods (the Hollywood Principle: “Don’t call us, we’ll call you”).

When to Use It

  • Multiple classes share the same algorithm structure but differ in specific steps
  • You want to enforce a consistent process across variations
  • You need to prevent subclasses from changing the algorithm’s overall flow
  • You want to extract common code from similar classes into a parent class

Visual Guide

Template Method Pattern Structure

classDiagram
    class AbstractClass {
        <<abstract>>
        +templateMethod() final
        #step1()*
        #step2()
        #step3()*
        #hook()
    }
    class ConcreteClassA {
        #step1()
        #step3()
        #hook()
    }
    class ConcreteClassB {
        #step1()
        #step3()
    }
    AbstractClass <|-- ConcreteClassA
    AbstractClass <|-- ConcreteClassB
    
    note for AbstractClass "templateMethod() calls:\n1. step1()\n2. step2()\n3. hook()\n4. step3()"

*The abstract class defines the template method that orchestrates the algorithm. Concrete classes implement the abstract steps (marked with ) and optionally override hooks.

Template Method Execution Flow

sequenceDiagram
    participant Client
    participant ConcreteClass
    participant AbstractClass
    
    Client->>ConcreteClass: templateMethod()
    ConcreteClass->>AbstractClass: templateMethod()
    AbstractClass->>ConcreteClass: step1() [abstract]
    ConcreteClass-->>AbstractClass: result
    AbstractClass->>AbstractClass: step2() [concrete]
    AbstractClass->>ConcreteClass: hook() [optional]
    ConcreteClass-->>AbstractClass: result
    AbstractClass->>ConcreteClass: step3() [abstract]
    ConcreteClass-->>AbstractClass: result
    AbstractClass-->>Client: final result

The template method in the parent class controls the flow, calling both concrete methods (implemented in parent) and abstract methods (implemented in subclasses).

Examples

Example 1: Data Processing Pipeline

Let’s build a data processing system where different file types follow the same workflow: read data, process it, and save results.

from abc import ABC, abstractmethod

class DataProcessor(ABC):
    """Abstract class defining the data processing template."""
    
    def process(self):
        """Template method - defines the algorithm skeleton."""
        print("Starting data processing...")
        
        data = self.read_data()
        print(f"Read data: {data}")
        
        processed = self.transform_data(data)
        print(f"Transformed data: {processed}")
        
        if self.should_validate():
            self.validate_data(processed)
        
        self.save_data(processed)
        print("Processing complete!\n")
    
    @abstractmethod
    def read_data(self):
        """Abstract method - subclasses must implement."""
        pass
    
    @abstractmethod
    def transform_data(self, data):
        """Abstract method - subclasses must implement."""
        pass
    
    @abstractmethod
    def save_data(self, data):
        """Abstract method - subclasses must implement."""
        pass
    
    def should_validate(self):
        """Hook method - subclasses can override (default: True)."""
        return True
    
    def validate_data(self, data):
        """Hook method - subclasses can override."""
        print("Validating data (default validation)...")


class CSVProcessor(DataProcessor):
    """Concrete implementation for CSV files."""
    
    def read_data(self):
        return "csv,data,rows"
    
    def transform_data(self, data):
        return data.upper()
    
    def save_data(self, data):
        print(f"Saving to CSV: {data}")
    
    def validate_data(self, data):
        print("CSV-specific validation: checking delimiter...")


class JSONProcessor(DataProcessor):
    """Concrete implementation for JSON files."""
    
    def read_data(self):
        return '{"key": "value"}'
    
    def transform_data(self, data):
        return data.replace('"', "'")
    
    def save_data(self, data):
        print(f"Saving to JSON: {data}")
    
    def should_validate(self):
        # Override hook to skip validation
        return False


# Usage
print("=== CSV Processing ===")
csv_processor = CSVProcessor()
csv_processor.process()

print("=== JSON Processing ===")
json_processor = JSONProcessor()
json_processor.process()

Expected Output:

=== CSV Processing ===
Starting data processing...
Read data: csv,data,rows
Transformed data: CSV,DATA,ROWS
Validating data (default validation)...
CSV-specific validation: checking delimiter...
Saving to CSV: CSV,DATA,ROWS
Processing complete!

=== JSON Processing ===
Starting data processing...
Read data: {"key": "value"}
Transformed data: {'key': 'value'}
Saving to JSON: {'key': 'value'}
Processing complete!

Key Points:

  • The process() method is the template method that defines the workflow
  • read_data(), transform_data(), and save_data() are abstract — subclasses must implement them
  • should_validate() and validate_data() are hooks — subclasses can optionally override them
  • The algorithm structure is consistent, but specific steps vary by file type

Try it yourself: Add an XMLProcessor class that reads XML data, transforms it by removing whitespace, and saves it with custom validation.


Example 2: Game Character AI

Let’s create an AI system for game characters where all characters follow the same decision-making process but with different behaviors.

from abc import ABC, abstractmethod
import random

class CharacterAI(ABC):
    """Template for character AI behavior."""
    
    def __init__(self, name):
        self.name = name
        self.health = 100
    
    def take_turn(self, enemy):
        """Template method - defines AI turn structure."""
        print(f"\n{self.name}'s turn:")
        
        self.assess_situation(enemy)
        
        if self.should_use_special_ability():
            self.use_special_ability(enemy)
        else:
            action = self.choose_action(enemy)
            self.execute_action(action, enemy)
        
        self.end_turn()
    
    def assess_situation(self, enemy):
        """Concrete method - common to all characters."""
        print(f"  Assessing: My HP={self.health}, Enemy HP={enemy.health}")
    
    @abstractmethod
    def choose_action(self, enemy):
        """Abstract method - each character type decides differently."""
        pass
    
    @abstractmethod
    def execute_action(self, action, enemy):
        """Abstract method - each character executes actions differently."""
        pass
    
    def should_use_special_ability(self):
        """Hook method - default implementation."""
        return self.health < 30
    
    def use_special_ability(self, enemy):
        """Hook method - can be overridden."""
        print("  Using generic special ability!")
    
    def end_turn(self):
        """Concrete method - common cleanup."""
        print(f"  {self.name} ends turn.")


class WarriorAI(CharacterAI):
    """Aggressive melee fighter."""
    
    def choose_action(self, enemy):
        if enemy.health < 40:
            return "heavy_attack"
        return "normal_attack"
    
    def execute_action(self, action, enemy):
        if action == "heavy_attack":
            damage = 30
            print(f"  {self.name} uses HEAVY ATTACK!")
        else:
            damage = 15
            print(f"  {self.name} attacks normally.")
        
        enemy.health -= damage
        print(f"  Dealt {damage} damage. Enemy HP: {enemy.health}")
    
    def use_special_ability(self, enemy):
        print(f"  {self.name} uses BERSERKER RAGE! (Double damage next turn)")


class MageAI(CharacterAI):
    """Strategic spellcaster."""
    
    def choose_action(self, enemy):
        if self.health > 70:
            return "offensive_spell"
        return "defensive_spell"
    
    def execute_action(self, action, enemy):
        if action == "offensive_spell":
            damage = 25
            enemy.health -= damage
            print(f"  {self.name} casts FIREBALL! Dealt {damage} damage.")
        else:
            heal = 20
            self.health = min(100, self.health + heal)
            print(f"  {self.name} casts HEAL! Restored {heal} HP.")
    
    def should_use_special_ability(self):
        # Mages use special ability more conservatively
        return self.health < 20
    
    def use_special_ability(self, enemy):
        print(f"  {self.name} casts METEOR STORM! (Massive AOE damage)")


class RogueAI(CharacterAI):
    """Cunning opportunist."""
    
    def choose_action(self, enemy):
        # Rogues choose randomly for unpredictability
        return random.choice(["backstab", "poison", "evade"])
    
    def execute_action(self, action, enemy):
        if action == "backstab":
            damage = 35
            enemy.health -= damage
            print(f"  {self.name} BACKSTABS! Critical hit: {damage} damage.")
        elif action == "poison":
            damage = 10
            enemy.health -= damage
            print(f"  {self.name} applies POISON. {damage} damage + DoT.")
        else:
            print(f"  {self.name} EVADES and repositions.")
    
    def should_use_special_ability(self):
        # Rogues never use the default special ability
        return False


# Simulate combat
warrior = WarriorAI("Thorin")
mage = MageAI("Gandalf")
rogue = RogueAI("Shadow")

# Create a dummy enemy
class Enemy:
    def __init__(self):
        self.health = 100

enemy = Enemy()

print("=== Combat Simulation ===")
warrior.take_turn(enemy)
mage.health = 60  # Simulate damage
mage.take_turn(enemy)
rogue.take_turn(enemy)

# Test special ability trigger
warrior.health = 25
print("\n=== Low Health Scenario ===")
warrior.take_turn(enemy)

Expected Output (Rogue actions vary due to randomness):

=== Combat Simulation ===

Thorin's turn:
  Assessing: My HP=100, Enemy HP=100
  Thorin attacks normally.
  Dealt 15 damage. Enemy HP: 85
  Thorin ends turn.

Gandalf's turn:
  Assessing: My HP=60, Enemy HP=85
  Gandalf casts HEAL! Restored 20 HP.
  Gandalf ends turn.

Shadow's turn:
  Assessing: My HP=100, Enemy HP=85
  Shadow applies POISON. 10 damage + DoT.
  Shadow ends turn.

=== Low Health Scenario ===

Thorin's turn:
  Assessing: My HP=25, Enemy HP=75
  Thorin uses BERSERKER RAGE! (Double damage next turn)
  Thorin ends turn.

Key Points:

  • All characters follow the same turn structure: assess → choose → execute → end
  • Each character class implements choose_action() and execute_action() differently
  • Hook methods (should_use_special_ability(), use_special_ability()) provide optional customization
  • The Rogue overrides should_use_special_ability() to completely disable the feature

Java/C++ Notes:

  • In Java, mark the template method as final to prevent overriding: public final void takeAction()
  • In C++, you can’t prevent overriding, but document the template method clearly
  • Use protected visibility for methods meant to be overridden by subclasses

Try it yourself: Add a HealerAI class that prioritizes healing when health is below 50% and only attacks when health is above 80%. Override the end_turn() method to display a healing message.

Common Mistakes

1. Making the Template Method Overridable

Mistake: Allowing subclasses to override the template method itself, which breaks the pattern’s purpose.

# WRONG - template method can be overridden
class DataProcessor(ABC):
    def process(self):  # Should be final/non-overridable
        self.read()
        self.transform()

class BadProcessor(DataProcessor):
    def process(self):  # Breaks the algorithm structure!
        self.transform()  # Skips read step

Why it’s wrong: The entire point is to enforce a consistent algorithm structure. If subclasses can override the template method, they can change the order of steps or skip steps entirely.

Fix: In Python, document that the template method shouldn’t be overridden. In Java, use the final keyword. In C++, document clearly and consider using the Non-Virtual Interface (NVI) idiom.


2. Making All Methods Abstract

Mistake: Defining every method as abstract, missing the opportunity to share common code.

# WRONG - no code reuse
class DataProcessor(ABC):
    @abstractmethod
    def read_data(self): pass
    
    @abstractmethod
    def validate_data(self): pass  # Could be shared!
    
    @abstractmethod
    def log_progress(self): pass  # Could be shared!

Why it’s wrong: You lose the benefits of code reuse. If validation and logging are identical across subclasses, implement them in the base class.

Fix: Only make methods abstract when they truly need different implementations. Provide concrete implementations for common behavior.


3. Confusing Template Method with Strategy Pattern

Mistake: Using Template Method when Strategy Pattern is more appropriate, or vice versa.

Template Method: Use when you have a fixed algorithm structure with varying steps (inheritance-based). Strategy Pattern: Use when you want to swap entire algorithms at runtime (composition-based).

# Template Method - algorithm structure is fixed
class ReportGenerator(ABC):
    def generate(self):
        self.gather_data()
        self.format_data()  # Step varies
        self.output()       # Step varies

# Strategy Pattern - entire algorithm can be swapped
class ReportGenerator:
    def __init__(self, formatter_strategy):
        self.formatter = formatter_strategy
    
    def generate(self):
        self.formatter.format_and_output()  # Entire process delegated

Why it matters: Template Method creates tight coupling through inheritance. Strategy Pattern offers more flexibility through composition. Choose based on whether you need a fixed process with variable steps (Template Method) or completely interchangeable algorithms (Strategy).


4. Forgetting to Provide Hook Methods

Mistake: Making every variable step abstract, forcing subclasses to implement methods they don’t need.

# WRONG - forces unnecessary implementation
class DataProcessor(ABC):
    @abstractmethod
    def pre_process(self): pass  # Not all processors need this
    
    @abstractmethod
    def post_process(self): pass  # Not all processors need this

class SimpleProcessor(DataProcessor):
    def pre_process(self):
        pass  # Empty implementation - waste of code
    
    def post_process(self):
        pass  # Empty implementation - waste of code

Fix: Use hook methods with default (often empty) implementations for optional steps.

# CORRECT - optional hooks
class DataProcessor(ABC):
    def pre_process(self):  # Hook with default empty implementation
        pass
    
    def post_process(self):  # Hook with default empty implementation
        pass

5. Creating Too Many Levels of Abstraction

Mistake: Building deep inheritance hierarchies with multiple template methods at different levels.

# WRONG - overly complex hierarchy
class BaseProcessor(ABC):  # Template method here
    def process(self): ...

class MiddleProcessor(BaseProcessor):  # Another template method here
    def advanced_process(self): ...

class ConcreteProcessor(MiddleProcessor):  # Confusing!
    def specific_process(self): ...

Why it’s wrong: Deep hierarchies become hard to understand and maintain. It’s unclear which template method controls what.

Fix: Keep hierarchies shallow (typically 2 levels: abstract base and concrete implementations). If you need more complexity, consider composition or other patterns.

Interview Tips

What Interviewers Look For

1. Pattern Recognition

Interviewers often present a scenario with code duplication and ask how you’d refactor it. Look for these signals:

  • Multiple classes with similar workflows but different details
  • Repeated algorithm structure across classes
  • Need to enforce a consistent process

Example question: “We have three report generators that all fetch data, format it, and export it, but each does formatting differently. How would you structure this?”

Answer approach: “I’d use the Template Method Pattern. Create an abstract ReportGenerator with a generate() template method that calls fetch_data(), format_data(), and export_data(). The format_data() method would be abstract, while fetch_data() and export_data() could be concrete if they’re shared.”


2. Explaining the Hollywood Principle

Be ready to explain: “Don’t call us, we’ll call you.” This is the inversion of control that defines Template Method.

Good explanation: “In Template Method, the parent class’s template method calls subclass methods, not the other way around. The parent controls the flow, and subclasses just fill in specific steps. This is the opposite of typical inheritance where subclasses call parent methods.”


3. Comparing with Strategy Pattern

This is a common interview question: “When would you use Template Method vs. Strategy Pattern?”

Strong answer:

  • Template Method: When you have a fixed algorithm structure and want to vary specific steps. Uses inheritance. Decided at compile-time.
  • Strategy Pattern: When you want to swap entire algorithms at runtime. Uses composition. More flexible but more complex.
  • Example: “For a payment processing system with fixed steps (validate, charge, notify), use Template Method. For a sorting system where you might switch between quicksort and mergesort at runtime, use Strategy.”

4. Discussing Trade-offs

Interviewers want to know you understand the pattern’s limitations:

Advantages:

  • Eliminates code duplication
  • Enforces consistent algorithm structure
  • Easy to add new variations (just add a subclass)

Disadvantages:

  • Creates tight coupling through inheritance
  • Can be harder to understand than simple duplication for simple cases
  • Violates Liskov Substitution Principle if subclasses change behavior significantly

When to avoid: “If the algorithm structure varies significantly between implementations, Template Method creates artificial constraints. Also avoid for very simple cases where the pattern adds more complexity than it removes.”


5. Live Coding Tips

If asked to implement Template Method:

  1. Start with the abstract class and template method first — this shows you understand the pattern’s structure
  2. Identify which methods should be abstract vs. concrete — explain your reasoning
  3. Add at least one hook method — shows you understand optional customization
  4. Make the template method non-overridable — in Python, add a docstring note; in Java, use final
  5. Create 2-3 concrete implementations — demonstrates the pattern’s value

Common interview scenario: “Design a testing framework where all tests follow the same lifecycle: setup, execute, teardown, but each test type has different execution logic.”

class TestCase(ABC):
    def run(self):  # Template method
        self.setup()
        try:
            self.execute()  # Abstract - varies by test type
        finally:
            self.teardown()
    
    def setup(self):  # Hook
        pass
    
    @abstractmethod
    def execute(self):
        pass
    
    def teardown(self):  # Hook
        pass

6. Red Flags to Avoid

  • Don’t say “Template Method and Strategy are the same” — they’re fundamentally different
  • Don’t make the template method abstract — it should be concrete and call other methods
  • Don’t forget to mention the Hollywood Principle — it’s a key concept
  • Don’t overcomplicate — if asked for a simple example, keep it simple

Practice Question:

“You’re building a data import system. All imports follow the same process: validate file, parse data, transform data, load into database. However, CSV, JSON, and XML files parse differently. How would you design this?”

Strong answer structure:

  1. Identify this as Template Method (fixed process, varying steps)
  2. Sketch the abstract class with import_data() as template method
  3. Make parse_data() abstract (varies by format)
  4. Make validate_file(), transform_data(), and load_to_db() concrete (shared logic)
  5. Add a hook like should_validate_schema() for optional behavior
  6. Mention you’d create CSVImporter, JSONImporter, XMLImporter subclasses

Key Takeaways

  • Template Method Pattern defines an algorithm’s skeleton in a base class, with subclasses implementing specific steps without changing the overall structure.

  • Use abstract methods for required variations (steps that must differ between subclasses) and hook methods for optional variations (steps that can optionally be customized).

  • The template method should be non-overridable to enforce the algorithm’s structure — in Java use final, in Python document clearly.

  • Template Method uses inheritance (compile-time binding) while Strategy Pattern uses composition (runtime binding) — choose Template Method when you have a fixed process with varying steps.

  • The Hollywood Principle (“Don’t call us, we’ll call you”) is key — the parent’s template method controls the flow and calls subclass methods, inverting the typical control flow of inheritance.