Template Method Pattern — Define Algorithm Skeleton, Let Subclasses Fill Details
What You'll Learn
You will learn how the Template Method pattern defines the invariant parts of an algorithm in a base class while letting subclasses override specific steps. You will also understand the distinction between abstract methods (must override) and hook methods (optional override).
Why It Matters
When multiple algorithms share the same overall structure but differ in individual steps, duplicating the structure across each implementation leads to maintenance nightmares. Template Method captures the skeleton once and allows controlled customisation at designated hook points. Consider data mining: loading data from CSV, JSON, and XML all follow "load, clean, parse, analyse, report" but each has different Parsing logic. Template Method keeps the pipeline structure in one place.
Real-World Use
Data mining frameworks follow a standard pipeline: load data, clean data, transform, analyse, visualise. Each step varies by data source. DodaTech's data ingestion framework provides a template for reading, validating, transforming, and loading data, with subclasses overriding only the source-specific steps. Adding a new data source like Parquet means writing a new subclass with just the loading and Parsing logic — the rest of the pipeline is inherited.
The Pattern
AbstractClass defines the Template Method containing the algorithm skeleton. Primitive operations are abstract; hook methods have default implementations that subclasses may override.
from abc import ABC, abstractmethod
class DataMiner(ABC):
def mine(self, path: str) -> str:
data = self.load_data(path)
cleaned = self.clean_data(data)
parsed = self.parse_data(cleaned)
result = self.analyze(parsed)
self.generate_report(result)
return self.get_report()
@abstractmethod
def load_data(self, path: str) -> str:
pass
def clean_data(self, data: str) -> str:
return data.strip()
@abstractmethod
def parse_data(self, data: str) -> list:
pass
def analyze(self, data: list) -> dict:
return {"count": len(data), "items": data}
def generate_report(self, result: dict):
print(f"Report: {result['count']} items found")
def get_report(self) -> str:
return "Mining complete"
class CSVMiner(DataMiner):
def load_data(self, path: str) -> str:
return "name,age\nAlice,30\nBob,25\n"
def parse_data(self, data: str) -> list:
lines = data.strip().split("\n")
return [line.split(",") for line in lines[1:]]
class JSONMiner(DataMiner):
def load_data(self, path: str) -> str:
return '[{"name":"Alice","age":30},{"name":"Bob","age":25}]'
def parse_data(self, data: str) -> list:
import json
return json.loads(data)
for miner in [CSVMiner(), JSONMiner()]:
print(type(miner).__name__, miner.mine("dummy.txt"))
print("---")
CSVMiner
Report: 2 items found
Mining complete
---
JSONMiner
Report: 2 items found
Mining complete
---
Structure
classDiagram
class AbstractClass {
+templateMethod()
#primitiveOperation1()
#primitiveOperation2()
#hook()
}
class ConcreteClassA {
#primitiveOperation1()
#primitiveOperation2()
}
class ConcreteClassB {
#primitiveOperation1()
#primitiveOperation2()
}
ConcreteClassA --|> AbstractClass
ConcreteClassB --|> AbstractClass
Real-World Usage
- Java
AbstractList— provides<a href="/design-patterns/iterator/">Iterator</a>()andlistIterator()templates; subclasses implementget()andsize(). - Python's
unittest.TestCase—setUp(),runTest(),tearDown()form the template of every test case. - Spring
JdbcTemplate— the Template Method handles connection management while subclasses provide SQL and row mapping. - Apache Camel routes —
configure()is a Template Method where route definitions are plugged into the engine.
Related Patterns
- Strategy uses composition to swap algorithms; Template Method uses inheritance to override steps.
- Factory Method is a specialised Template Method for object creation.
- Adapter can be used within a Template Method to adapt non-conforming step implementations.
- Visitor can be invoked from a template's hook points.
Pros and Cons
| Pros | Cons |
|---|---|
| Reuses common algorithm structure | Increases inheritance depth |
| Controlled customisation at hook points | Hard to follow the flow across inheritance levels |
| Follows the Hollywood Principle | Every subclass must implement all abstract steps |
| Core algorithm is protected from modification | Template methods can become too rigid for complex variations |
The data miner example shows the Template Method mine() which calls a sequence of steps. load_data() and parse_data() are abstract — each subclass must implement them. clean_data() and analyze() have default implementations — subclasses can override if needed. This is the Hollywood Principle in action: "Don't call us, we'll call you."
Practice Questions
- Implement a
Gametemplate withinitialize(),playTurn(), andendGame()steps — then create Chess and Monopoly variants. - How would you decide whether a step should be abstract (must override), virtual with default (can override), or a hook (optional no-op)?
- What are the risks of adding new steps to the Template Method after subclasses have been written?
- Compare Template Method with Strategy — when would you choose one over the other for an algorithm that varies in multiple steps?
Challenge
Refactor the DataMiner template to support an optional validate_data() hook step between cleaning and Parsing. Ensure existing subclasses work without modification.
Real-World Task
Identify a duplicated algorithm structure in your codebase — two or more methods that follow the same sequence of operations with variant steps. Refactor them using Template Method and use DodaTech's code lineage tool to track subclass overrides.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro