Chapter 05 · Article 25 of 55
Template Method Pattern
Type: Behavioural Design Pattern Also Known As: Hot Spot Pattern Complexity: Popularity:
Article outline14 sections on this page+
Overview
Type: Behavioural Design Pattern
Also Known As: Hot Spot Pattern
Complexity: ***
Popularity:
Intent
Define the skeleton of an algorithm in a method, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's overall structure.
The pattern encapsulates the invariant parts of an algorithm in a base class while leaving the variable parts to be implemented by derived classes. The template method itself is typically declared final (or sealed) to prevent subclasses from altering the algorithm's sequence - only the individual steps can be overridden.
Motivation
Consider a scenario where multiple classes perform similar operations following the same high-level sequence but differ in specific low-level details. Without Template Method, each class duplicates the algorithm's skeleton, leading to maintenance nightmares and inconsistency. The pattern extracts the common structure into a single location, enforcing consistency while preserving flexibility.
Problem It Solves
-
Code Duplication in Similar Algorithms - When several classes implement variations of the same algorithm, the shared logic gets copy-pasted across all implementations. Any bug fix or enhancement must be replicated everywhere.
-
Enforcing Algorithm Structure - Without a fixed skeleton, subclasses might accidentally skip steps, reorder them, or introduce inconsistencies. Template Method guarantees the sequence is always followed.
-
Uncontrolled Extension Points - Frameworks need to let users customize behaviour at specific points without giving them the ability to break the overall workflow. Template Method provides controlled "hot spots" for customization.
-
Violation of the Open/Closed Principle - Modifying existing code to add new variations violates OCP. Template Method allows extension through subclassing without modifying the base algorithm.
When to Use / When NOT to Use
| When to Use | When NOT to Use |
|---|---|
| Multiple classes share the same algorithm structure but differ in specific steps | The algorithm varies significantly between implementations with no common skeleton |
| You want to enforce a fixed sequence of operations | You need to swap algorithms at runtime (prefer Strategy) |
| Framework design where users extend behaviour at predefined hook points | The number of variations is small and unlikely to grow |
| You want to apply the Hollywood Principle - base class calls subclass, not vice versa | Composition would be simpler and more flexible than inheritance |
| Common pre/post-processing logic surrounds variable core logic | Deep inheritance hierarchies already exist (adding more layers increases fragility) |
| You need to prevent subclasses from changing the algorithm's order | You need multiple independent variation axes (prefer Bridge or Decorator) |
Key Concepts & Theory
Template Method (Final/Sealed)
The template method defines the algorithm's skeleton as a sequence of steps. It is declared final (Java), sealed (C#), or non-virtual to prevent subclasses from overriding the sequence itself. Only the individual steps are open for customization.
Abstract Steps (Primitive Operations)
These are methods that must be overridden by every concrete subclass. They represent the variable parts of the algorithm that have no sensible default implementation.
Concrete Steps (Invariant Operations)
Steps with fixed implementations in the base class that subclasses cannot override. These represent the invariant parts of the algorithm shared across all variations.
Hook Methods
Optional methods with a default (often empty) implementation in the base class. Subclasses may override them to inject additional behaviour at specific points. Hooks provide optional extension without forcing implementation.
// Hook example - subclass can optionally override
protected boolean customerWantsCondiments() {
return true; // default: yes
}
The Hollywood Principle
"Don't call us, we'll call you."
The base class (high-level component) calls the subclass methods (low-level components), not the other way around. This inverts the typical control flow - the framework drives execution and calls into user code at the appropriate moments. This principle reduces coupling and prevents "dependency rot" where high-level and low-level components become circularly dependent.
ASCII Class Diagram
┌─────────────────────────────────────┐
│ AbstractClass │
├─────────────────────────────────────┤
│ + templateMethod() [final] │──────────────────────────┐
│ # step1() [abstract] │ │
│ # step2() [abstract] │ templateMethod() { │
│ # step3() [concrete] │ step1(); │
│ # hook() [virtual/empty] │ step2(); │
├─────────────────────────────────────┤ step3(); │
│ │ if (hook()) │
└──────────┬──────────────┬───────────┘ step4(); │
│ │ } │
│ │ ────────────────────────┘
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ ConcreteClassA │ │ ConcreteClassB │
├──────────────────┤ ├──────────────────┤
│ # step1() {...} │ │ # step1() {...} │
│ # step2() {...} │ │ # step2() {...} │
│ # hook() {...} │ │ │ ← uses default hook
└──────────────────┘ └──────────────────┘
Key relationships:
AbstractClassowns the template method and defines the algorithm skeletonConcreteClassA/Boverride abstract steps and optionally override hooks- The template method is
final- subclasses cannot change the call sequence
Pseudocode Implementation
Example 1: Data Mining Framework
abstract class DataMiner {
// Template method - FINAL, cannot be overridden
final method mine(path: String) {
file = openFile(path)
rawData = extractData(file)
data = parseData(rawData)
analysis = analyzeData(data)
sendReport(analysis)
closeFile(file)
}
// Abstract steps - MUST be implemented by subclasses
abstract method openFile(path: String): File
abstract method extractData(file: File): RawData
abstract method parseData(raw: RawData): Data
// Concrete step - shared across all subclasses
method analyzeData(data: Data): Analysis {
// Common analysis logic: compute averages, find anomalies
return StandardAnalyzer.analyze(data)
}
// Concrete step - shared default reporting
method sendReport(analysis: Analysis) {
EmailService.send(admin, analysis.summary())
}
// Hook - subclasses may override
method closeFile(file: File) {
file.close()
}
}
class CSVMiner extends DataMiner {
method openFile(path: String): File {
return CSVReader.open(path)
}
method extractData(file: File): RawData {
return file.readAllLines()
}
method parseData(raw: RawData): Data {
rows = []
for each line in raw:
rows.add(line.split(","))
return Data(rows)
}
}
class PDFMiner extends DataMiner {
method openFile(path: String): File {
return PDFLibrary.open(path)
}
method extractData(file: File): RawData {
return file.extractText() // OCR if needed
}
method parseData(raw: RawData): Data {
return PDFParser.structuredParse(raw)
}
// Override hook - PDF files need explicit resource release
method closeFile(file: File) {
file.releaseResources()
file.close()
}
}
// Client code
miner = new CSVMiner()
miner.mine("/data/sales-2026.csv")
miner = new PDFMiner()
miner.mine("/data/report-q1.pdf")
Example 2: Game AI Turn System
abstract class GameAI {
// Template method
final method takeTurn() {
collectResources()
buildStructures()
buildUnits()
attack()
}
// Concrete step - all AI types collect resources the same way
method collectResources() {
for each structure in ownedStructures:
structure.collect()
}
// Abstract steps - each AI type has unique strategy
abstract method buildStructures()
abstract method buildUnits()
// Hook with default behaviour
method attack() {
// Default: attack nearest enemy
enemy = findNearestEnemy()
if (enemy != null)
sendWarriors(enemy.position)
}
}
class OrcsAI extends GameAI {
method buildStructures() {
if (hasResources()):
build(Barracks)
build(Farm)
}
method buildUnits() {
if (hasFreeBarracks()):
train(Warrior)
}
// Override attack - orcs are aggressive
method attack() {
enemies = findAllEnemies()
sendAllUnits(enemies.strongest().position)
}
}
class HumansAI extends GameAI {
method buildStructures() {
if (hasResources()):
build(Castle)
build(Wall)
}
method buildUnits() {
if (hasFreeBarracks()):
train(Archer)
train(Knight)
}
// Uses default attack behaviour
}
Template Method vs Strategy
| Aspect | Template Method | Strategy |
|---|---|---|
| Mechanism | Inheritance | Composition |
| Binding Time | Compile-time (static) | Runtime (dynamic) |
| Granularity | Varies individual steps of an algorithm | Replaces the entire algorithm |
| Algorithm Ownership | Base class owns the skeleton | Client or context owns the selection |
| Flexibility | Limited to subclass hierarchy | Any conforming strategy can be injected |
| Code Reuse | Shared logic lives in base class | Each strategy is self-contained |
| Number of Classes | One subclass per variation | One strategy class per variation |
| Control Flow | Hollywood Principle (base calls subclass) | Client calls strategy directly |
| Coupling | Tight (subclass coupled to base class) | Loose (interface-based) |
| Testing | Harder (must instantiate concrete subclass) | Easier (mock/stub strategies independently) |
| When to Prefer | Fixed algorithm structure with variable steps | Interchangeable algorithms, runtime switching |
| Violation Risk | Fragile Base Class problem | Strategy explosion (too many small classes) |
Rule of thumb: Use Template Method when the algorithm's structure is invariant and you want to enforce it. Use Strategy when you need runtime flexibility or want to avoid inheritance hierarchies.
Real-World Examples
JUnit Framework - setUp() / tearDown()
JUnit's TestCase class uses Template Method. The runBare() method defines the skeleton: call setUp(), run the test method, call tearDown(). Test authors override setUp() and tearDown() without altering the execution sequence.
Java Servlet - doGet() / doPost()
HttpServlet.service() is the template method that dispatches to doGet(), doPost(), doPut(), etc. based on the HTTP method. Developers override specific handlers without touching the dispatch logic.
React Component Lifecycle
React class components follow Template Method: componentDidMount(), shouldComponentUpdate(), render(), componentWillUnmount() are hooks called by React's reconciliation algorithm. Developers override specific lifecycle methods; React controls the invocation order.
Build Systems (Maven Lifecycle)
Maven's build lifecycle (validate → compile → test → package → install → deploy) is a template method. Plugins bind to specific phases (steps) without altering the overall build sequence.
Java's AbstractList / AbstractMap
These abstract classes implement most List/Map operations in terms of a few abstract methods (get(), size()). Subclasses only implement the primitives; the template handles iteration, equality, hashing, etc.
Advantages & Disadvantages
| Advantages | Disadvantages |
|---|---|
| Eliminates code duplication by extracting common algorithm into base class | Tight coupling between base and subclasses via inheritance |
| Enforces a consistent algorithm structure across all variations | Fragile Base Class problem - changes to base can break subclasses |
| Follows Open/Closed Principle - extend via subclassing, not modification | Can lead to deep inheritance hierarchies |
| Provides controlled extension points (hooks) for optional customization | Subclasses may be confused about which methods to override |
| Implements Hollywood Principle - reduces dependency rot | Harder to understand control flow (inverted via callbacks) |
| Easy to add new variations without modifying existing code | Limited to single inheritance in languages without mixins |
| Simplifies client code - just instantiate the right subclass | Too many abstract methods make subclassing burdensome |
| Natural fit for framework design | Runtime algorithm switching is impossible without restructuring |
Constraints & Edge Cases
Fragile Base Class Problem
Modifying the template method or adding new steps in the base class can silently break all existing subclasses. Mitigation: keep the template method stable, version it carefully, and document the contract thoroughly.
Too Many Abstract Methods
If the base class declares too many abstract steps, subclasses become burdensome to implement. Every new subclass must provide implementations for all abstract methods, even if some are trivial. Mitigation: provide sensible defaults (make them hooks instead of abstract methods) and only force overriding where truly necessary.
Hook Abuse
Hooks with empty default implementations can be misused to inject arbitrary behaviour that violates the algorithm's intent. Subclasses might use hooks to skip steps or introduce side effects. Mitigation: document hook contracts clearly and validate post-conditions in the template method.
Liskov Substitution Principle (LSP) Concerns
Subclass step implementations must honour the base class's contract. If parseData() is expected to return non-null structured data, a subclass returning null violates LSP and breaks the template method's subsequent steps. Mitigation: use assertions/contracts in the template method to validate step outputs.
Thread Safety
If the template method uses shared mutable state, concurrent execution by multiple threads can cause race conditions. Each step might assume exclusive access. Mitigation: make template methods stateless or synchronize appropriately.
Diamond Inheritance (Multiple Inheritance Languages)
In C++ or Python, multiple inheritance can create ambiguity about which base class's template method or step implementation to use. Mitigation: use virtual inheritance or prefer composition.
Interview Follow-ups
Q1: How does Template Method differ from Factory Method?
Answer: Factory Method is actually a specialization of Template Method. In Factory Method, the "step" being deferred to subclasses is specifically object creation. Template Method is the general pattern - any algorithmic step can be deferred. Factory Method focuses on what to create; Template Method focuses on how to execute an algorithm.
Q2: Can you combine Template Method with other patterns?
Answer: Yes, commonly combined with:
- Factory Method - a step in the template creates objects via Factory Method
- Strategy - individual steps delegate to strategy objects for runtime flexibility (hybrid approach)
- Observer - hooks notify observers at specific algorithm points
- Decorator - wrap the template method to add cross-cutting concerns (logging, timing)
Q3: How would you refactor a Template Method to use composition instead?
Answer: Replace inheritance with a configuration object containing function references (lambdas/callbacks) for each variable step. The former base class accepts this configuration and calls the functions at appropriate points. This transforms Template Method into a Strategy-like approach, gaining runtime flexibility at the cost of losing the enforced type contract that abstract methods provide.
Hints-Only Questions
H1: "Design a report generation system where PDF, HTML, and Excel reports share the same generation pipeline but differ in rendering."
Hints:
- Think about what steps are common (fetch data, validate, format headers, render body, add footer) vs. what varies (rendering)
- Consider which steps should be abstract vs. hooks - does every format need custom header logic?
H2: "You have a legacy system with 12 subclasses of a template method. A new requirement needs a step inserted between step 2 and step 3. How do you handle this?"
Hints:
- Consider backward compatibility - can the new step be a hook with a no-op default?
- Think about the Fragile Base Class problem and how to test all 12 subclasses after the change
Counter Questions to Ask Interviewer
-
"Is the algorithm's structure truly fixed, or might the sequence of steps need to vary between implementations?" - Determines if Template Method is appropriate or if Strategy/Chain of Responsibility fits better.
-
"How many variations do we expect, and will they grow over time?" - A small, stable set might not justify the pattern's overhead; a growing set validates it.
-
"Are there multiple independent axes of variation?" - If yes, Template Method alone leads to class explosion; Bridge or composition might be needed.
-
"Do we need to switch behaviour at runtime?" - Template Method binds at compile-time via inheritance; runtime switching requires Strategy or a hybrid.
-
"What's the team's comfort level with inheritance hierarchies?" - Deep hierarchies are harder to maintain; if the team prefers composition, a different approach may be more sustainable.
References & Whitepapers
-
Gamma, E., Helm, R., Johnson, R., Vlissides, J. - Design Patterns: Elements of Reusable Object-Oriented Software (GoF, 1994), Chapter 5: Behavioral Patterns - Template Method.
-
Cwalina, K., Abrams, B. - Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries (2nd Edition, 2008) - Discusses template method in the context of framework extensibility.
-
Martin, R.C. - Agile Software Development: Principles, Patterns, and Practices (2002) - Template Method and the Hollywood Principle in the context of SOLID.
-
Bloch, J. - Effective Java (3rd Edition, 2018), Item 20 - Prefer interfaces to abstract classes; discusses trade-offs relevant to Template Method.
-
Mikhajlov, L., Sekerinski, E. - "A Study of the Fragile Base Class Problem" (ECOOP 1998) - Formal analysis of the risks inherent in inheritance-based patterns like Template Method.
-
Freeman, E., Robson, E. - Head First Design Patterns (2nd Edition, 2020) - Chapter 8: The Template Method Pattern with the Hollywood Principle.
Related Topics
- Strategy Pattern - Composition-based alternative for interchangeable algorithms
- Factory Method Pattern - Specialization of Template Method for object creation
- Observer Pattern - Can be combined with hooks for event notification
- Decorator Pattern - Wraps behaviour without inheritance
- Command Pattern - Encapsulates operations as objects; alternative to step callbacks
- Hollywood Principle & IoC - The broader principle Template Method implements
- Open/Closed Principle - Template Method as an OCP enabler