Template Method Pattern
Define the skeleton of an algorithm in a base class and let subclasses override specific steps without changing the algorithm's overall structure.
Overview
Almost every non-trivial algorithm has two layers: the invariant structure that never changes, and the variable steps that differ by context. Mixing both layers in a single class forces you to duplicate the invariant parts whenever a new variation appears.
The Template Method pattern is a behavioral design pattern that separates these layers cleanly. A base class defines the algorithm's skeleton — a fixed sequence of steps — while subclasses fill in only the steps that need to vary. The overall flow stays in one place and cannot be accidentally broken by a subclass.
This pattern is one of the most natural expressions of the open/closed principle: the algorithm is closed for modification (the skeleton is not touched) but open for extension (each step can be overridden independently).
Problem & Motivation
Imagine you are building a data pipeline that processes reports from different sources: CSV files, JSON APIs, and database queries. Each pipeline follows the same structure:
- Open the data source
- Parse the raw data into records
- Validate each record
- Write the results to an output destination
- Close the data source
The open/close logic and the write-to-output logic are identical across all three pipelines. Only the parse and validate steps differ — CSV parsing is different from JSON deserialization, and a CSV row has different validation rules than a database row.
Without Template Method, you copy the scaffolding into every pipeline class. When the scaffolding needs to change (say, you add logging between steps), you must update every copy. The pattern eliminates that duplication by placing the invariant sequence in exactly one method.
Class Diagram
Loading diagram...
The key structural points are:
run()is the template method — it calls the steps in a fixed order and is declaredfinal(or its equivalent) so subclasses cannot reorder the steps.- Methods marked with
*are abstract hooks that every subclass must implement. validate()andclose()have default implementations in the base class; subclasses may override them but are not required to.
Implementation
The implementations below model a DataPipeline that processes report data. All four versions follow the same algorithm: open, parse, validate, write, close. CsvPipeline overrides all variable steps; JsonPipeline inherits the default validate() and close() implementations.
// Base class defines the algorithm skeleton
abstract class DataPipeline {
// Template method — sealed to preserve the algorithm structure
run(): void {
this.open();
this.parse();
this.validate();
this.write();
this.close();
}
protected abstract open(): void;
protected abstract parse(): void;
protected abstract write(): void;
// Default implementations — subclasses may override
protected validate(): void {
console.log(" [default] Validating records...");
}
protected close(): void {
console.log(" [default] Closing data source.");
}
}
class CsvPipeline extends DataPipeline {
protected open(): void {
console.log("CSV: Opening file report.csv");
}
protected parse(): void {
console.log("CSV: Parsing comma-separated rows");
}
protected validate(): void {
console.log("CSV: Checking required columns are present");
}
protected write(): void {
console.log("CSV: Writing records to output table");
}
}
class JsonPipeline extends DataPipeline {
protected open(): void {
console.log("JSON: Connecting to REST API endpoint");
}
protected parse(): void {
console.log("JSON: Deserializing JSON payload");
}
protected write(): void {
console.log("JSON: Inserting records into database");
}
// validate() and close() inherited from DataPipeline
}
// Usage
console.log("--- CSV Pipeline ---");
new CsvPipeline().run();
console.log("\n--- JSON Pipeline ---");
new JsonPipeline().run();The Hollywood Principle
Template Method is a classic example of the Hollywood Principle: "Don't call us, we'll call you." The base class controls the flow and calls into the subclass at the right moment — the subclass never drives the algorithm. This inversion keeps orchestration logic in one place and prevents subclasses from accidentally breaking the contract by running steps in the wrong order.
Real-World Examples
Test frameworks — JUnit's TestCase class is a canonical example. The framework calls setUp(), then your test method, then tearDown() — always in that order. You override only the parts you care about. The framework owns the lifecycle; you own the test logic.
Web framework request handling — Django's class-based views use Template Method extensively. View.dispatch() calls get(), post(), put(), etc. based on the HTTP method. You subclass and override only the HTTP methods your endpoint needs. The authentication, permission checks, and response rendering happen in the skeleton that you never touch.
Document generation and export — Reporting libraries like Apache POI define an abstract ReportGenerator with a fixed sequence: open document, write header, write body, write footer, save. Subclasses for PDF, DOCX, and HTML each override the formatting steps while the structural flow is shared. Adding a new format means adding a new subclass, not modifying existing code.
Build tools — Maven's build lifecycle (validate → compile → test → package → install → deploy) is a template method. Each plugin hooks into specific phases — your code only touches the phases it cares about, and Maven owns the overall sequence.
Pros and Cons
Advantages
Eliminates code duplication — the invariant parts of the algorithm live in exactly one place. When the skeleton changes, you update one method, not every subclass.
Enforces algorithm contracts — marking the template method final in Java (or relying on convention in Python/Go) prevents subclasses from accidentally reordering steps or skipping mandatory ones.
Easy to extend — adding a new variation means adding a new subclass. No existing code is touched, satisfying the open/closed principle.
Selective override — hooks with default implementations let subclasses override only what they need, keeping subclass code minimal.
Disadvantages
Inheritance coupling — the pattern depends on class inheritance. Subclasses are tightly coupled to the base class; changing the base class signature cascades to all subclasses.
Difficult to follow control flow — the algorithm is split across files. Readers must mentally jump between the base class and each subclass to understand what actually runs, which can be disorienting in deep hierarchies.
Liskov Substitution violations — a poorly designed subclass can override a hook in a way that silently violates the algorithm's preconditions, causing subtle bugs that are hard to attribute to the pattern.
Can lead to fragile base class problem — adding a new step to the template method in the base class can break all existing subclasses that did not expect the new call.
When to Use / When to Avoid
Use Template Method when:
- Multiple classes share the same algorithmic structure but differ only in specific steps.
- You want to give library users extension points without letting them change the overall algorithm sequence.
- Duplicated scaffolding code is the dominant maintenance cost — the pattern directly removes it.
- The number of variable steps is small and well-defined.
Avoid Template Method when:
- The variations are few and unlikely to grow — a simple conditional or a well-placed function parameter is less coupling than a new subclass hierarchy.
- You need to vary the combination of steps at runtime, not at class definition time — Strategy is a better fit because it composes behavior rather than inheriting it.
- The algorithm steps need to be shared across unrelated class hierarchies — inheritance ties you to a single hierarchy, whereas an interface-based approach (Strategy or functional composition) crosses hierarchy boundaries.
- The codebase is written in a functional style where higher-order functions and closures are idiomatic — passing step functions directly is simpler and has less overhead than building abstract classes.
Related Patterns
Strategy — the closest alternative. Both patterns factor out variable behavior, but Strategy does so with composition rather than inheritance. Instead of subclassing, you inject a strategy object that implements the variable step. Prefer Strategy when you need to swap behavior at runtime, or when the varying steps are orthogonal and can be mixed independently.
Factory Method — often used inside Template Method. When one of the algorithm's steps is "create an object," the base class can declare an abstract factory method, letting subclasses decide which concrete type to instantiate. The two patterns collaborate naturally.
Hook Method — not a separate GoF pattern, but a technique within Template Method. A hook is an optional step with a do-nothing default in the base class. Subclasses override hooks to participate in the algorithm without being forced to. Hooks give the pattern flexibility without making every step mandatory.
Builder — also structures a multi-step process, but the client drives the step sequence explicitly by calling builder methods. Template Method is the inverse: the base class calls the steps; the client never sees the sequence.
Design Tip
Before reaching for Template Method, ask whether Strategy would serve better. If you find yourself writing if (pipeline instanceof CsvPipeline) anywhere in your codebase, or if you expect to swap algorithms at runtime, switch to Strategy. Template Method shines when the algorithm structure is fixed and the variations are truly about "what happens in step N" rather than "which algorithm to use overall."