Template Method
Template Method fixes the overall shape of an algorithm in a base class while leaving individual steps for subclasses to fill in, so the sequence of operations never changes even though its details do.
Problem
Imagine you're building a reporting tool that generates documents in different formats: HTML and Markdown. Both formats follow the same overall structure - a title header, a list of content rows, and a footer - but the exact markup for each step differs.
You could copy the entire algorithm into each report class. But then changing the overall flow (say, adding a timestamp section) requires modifying every class individually.
Alternatively, you could pack all the logic into one class and use conditionals to choose the output format - but that violates the Open/Closed Principle every time a new format is added.
Solution
The Template Method pattern suggests breaking the algorithm into steps, turning each step into a method, and putting a series of calls to those methods inside a single template method in the abstract class.
The steps may either be `abstract` (forcing subclasses to provide an implementation) or have a default implementation that subclasses can optionally override (hooks).
Subclasses can override specific steps without touching the template method itself. This guarantees that the overall algorithm structure stays the same while individual steps remain customizable.
When to Use
- Reach for Template Method when subclasses should be free to customize a few specific steps of an algorithm, but must not be able to change its overall sequence.
- It also fits when you notice several classes implementing almost the same algorithm with small variations, which today forces you to update every copy whenever the shared logic changes.
Real-World Examples
- **Data parsers** - a document parser follows the same steps (open, parse, close) for CSV, JSON, and XML files, but each format requires different parsing logic.
- **Web frameworks** - request handling pipelines (authenticate, authorize, handle, respond) define a skeleton that controllers override with specific handler logic.
- **Game AI** - a turn-based AI defines a fixed turn structure (collect resources, build, attack) while different difficulty levels override each step with varying strategies.
Structure
The AbstractClass declares the template method and all algorithm steps. Some steps are abstract; others are hooks with empty default implementations. ConcreteClass overrides the steps it needs to customize.
Participants
Declares the template method that defines the algorithm skeleton. Also declares abstract primitive operations that subclasses must implement, and optional hook methods with empty or default implementations.
In the diagram: ReportGenerator.
Implements the abstract primitive operations to carry out the subclass-specific steps of the algorithm. Does not override the template method itself.
In the diagram: HtmlReport, MarkdownReport.
class ReportGenerator {
generate(title, rows) {
const parts = [
this.header(title),
this.body(rows),
this.footer(),
];
return parts.filter(Boolean).join('\n');
}
header(title) { return ''; }
footer() { return ''; }
body(rows) {
throw new Error('body() must be implemented');
}
}
class HtmlReport extends ReportGenerator {
header(title) { return `<h1>${title}</h1><ul>`; }
body(rows) { return rows.map(r => ` <li>${r}</li>`).join('\n'); }
footer() { return '</ul>'; }
}
class MarkdownReport extends ReportGenerator {
header(title) { return `# ${title}\n`; }
body(rows) { return rows.map(r => `- ${r}`).join('\n'); }
}
const rows = ['Design Patterns', 'Clean Code', 'SOLID'];
const html = new HtmlReport();
console.log(html.generate('Books', rows));
const md = new MarkdownReport();
console.log(md.generate('Books', rows));
Step 1 of 6
generate() is the template method - it fixes the algorithm's skeleton
`generate` always calls `this.header(title)`, `this.body(rows)`, `this.footer()` in that order, filters out empty strings, and joins them - subclasses can't reorder or skip these steps, only fill in what each one produces.
Step 2 of 6
header/footer are optional hooks, body is a required hook
`header` and `footer` default to returning `''` (safe to skip), while `body` throws if not overridden - the base class distinguishes steps every report needs from steps that are merely optional.
Step 3 of 6
HtmlReport overrides all three hooks
It supplies `header`, `body` and `footer` to wrap the rows in `
`/``/`- ` tags - `generate` itself is never touched or repeated here.
Step 4 of 6
MarkdownReport only overrides header and body, inheriting the default footer
It skips overriding `footer`, so `generate` will call the base class's version that returns `''`, which then gets filtered out by `parts.filter(Boolean)`.
Step 5 of 6
html.generate() runs the fixed algorithm using HTML-specific steps
`html.generate('Books', rows)` calls the inherited `generate`, which invokes `HtmlReport`'s `header`, `body` and `footer` in sequence, producing a full `
...
- ...
Step 6 of 6
md.generate() runs the exact same algorithm, producing Markdown instead
`md.generate('Books', rows)` goes through the identical `header → body → footer → filter → join` sequence in `generate`, but since `MarkdownReport` fills the hooks differently, the output is a `# Books` heading with `- ` list items and no closing footer line.
abstract class ReportGenerator {
generate(title: string, rows: string[]): string {
const parts = [
this.header(title),
this.body(rows),
this.footer(),
];
return parts.filter(Boolean).join('\n');
}
protected header(_title: string): string { return ''; }
protected footer(): string { return ''; }
protected abstract body(rows: string[]): string;
}
class HtmlReport extends ReportGenerator {
protected header(title: string): string {
return `<h1>${title}</h1><ul>`;
}
protected body(rows: string[]): string {
return rows.map(r => ` <li>${r}</li>`).join('\n');
}
protected footer(): string { return '</ul>'; }
}
class MarkdownReport extends ReportGenerator {
protected header(title: string): string { return `# ${title}\n`; }
protected body(rows: string[]): string {
return rows.map(r => `- ${r}`).join('\n');
}
}
const rows = ['Design Patterns', 'Clean Code', 'SOLID'];
const html = new HtmlReport();
console.log(html.generate('Books', rows));
const md = new MarkdownReport();
console.log(md.generate('Books', rows));
abstract class ReportGenerator {
public final String generate(String title, String[] rows) {
StringBuilder sb = new StringBuilder();
String h = header(title);
if (!h.isEmpty()) sb.append(h).append('\n');
sb.append(body(rows));
String f = footer();
if (!f.isEmpty()) sb.append('\n').append(f);
return sb.toString();
}
protected String header(String title) { return ""; }
protected String footer() { return ""; }
protected abstract String body(String[] rows);
}
class HtmlReport extends ReportGenerator {
protected String header(String title) { return "<h1>" + title + "</h1><ul>"; }
protected String body(String[] rows) {
StringBuilder sb = new StringBuilder();
for (String r : rows) sb.append(" <li>").append(r).append("</li>\n");
return sb.toString().stripTrailing();
}
protected String footer() { return "</ul>"; }
}
class MarkdownReport extends ReportGenerator {
protected String header(String title) { return "# " + title + "\n"; }
protected String body(String[] rows) {
StringBuilder sb = new StringBuilder();
for (String r : rows) sb.append("- ").append(r).append('\n');
return sb.toString().stripTrailing();
}
}
String[] rows = {"Design Patterns", "Clean Code", "SOLID"};
System.out.println(new HtmlReport().generate("Books", rows));
System.out.println(new MarkdownReport().generate("Books", rows));
abstract class ReportGenerator
{
public string Generate(string title, string[] rows)
{
var parts = new[] { Header(title), Body(rows), Footer() };
return string.Join("\n", parts.Where(p => !string.IsNullOrEmpty(p)));
}
protected virtual string Header(string title) => "";
protected virtual string Footer() => "";
protected abstract string Body(string[] rows);
}
class HtmlReport : ReportGenerator
{
protected override string Header(string title) => $"<h1>{title}</h1><ul>";
protected override string Body(string[] rows) =>
string.Join("\n", rows.Select(r => $" <li>{r}</li>"));
protected override string Footer() => "</ul>";
}
class MarkdownReport : ReportGenerator
{
protected override string Header(string title) => $"# {title}\n";
protected override string Body(string[] rows) =>
string.Join("\n", rows.Select(r => $"- {r}"));
}
string[] rows = { "Design Patterns", "Clean Code", "SOLID" };
Console.WriteLine(new HtmlReport().Generate("Books", rows));
Console.WriteLine(new MarkdownReport().Generate("Books", rows));
from __future__ import annotations
from abc import ABC, abstractmethod
class ReportGenerator(ABC):
def generate(self, title: str, rows: list[str]) -> str:
parts = [self.header(title), self.body(rows), self.footer()]
return '\n'.join(p for p in parts if p)
def header(self, title: str) -> str: return ''
def footer(self) -> str: return ''
@abstractmethod
def body(self, rows: list[str]) -> str: ...
class HtmlReport(ReportGenerator):
def header(self, title: str) -> str:
return f'<h1>{title}</h1><ul>'
def body(self, rows: list[str]) -> str:
return '\n'.join(f' <li>{r}</li>' for r in rows)
def footer(self) -> str:
return '</ul>'
class MarkdownReport(ReportGenerator):
def header(self, title: str) -> str:
return f'# {title}\n'
def body(self, rows: list[str]) -> str:
return '\n'.join(f'- {r}' for r in rows)
rows = ['Design Patterns', 'Clean Code', 'SOLID']
html = HtmlReport()
print(html.generate('Books', rows))
md = MarkdownReport()
print(md.generate('Books', rows))
Step 1 of 6
generate() is the template method - it fixes the algorithm's skeleton
`generate` always calls `self.header(title)`, `self.body(rows)`, `self.footer()` in that order, filters truthy parts, and joins them - subclasses can't reorder or skip these steps, only fill in what each one produces.
Step 2 of 6
header/footer are optional hooks, body is a required hook
`header` and `footer` default to returning `''` (safe to skip), while `body` is `@abstractmethod` - the base class distinguishes steps every report needs from steps that are merely optional.
Step 3 of 6
HtmlReport overrides all three hooks
It supplies `header`, `body` and `footer` to wrap the rows in `
`/``/`- ` tags - `generate` itself is never touched or repeated here.
Step 4 of 6
MarkdownReport only overrides header and body, inheriting the default footer
It skips overriding `footer`, so `generate` will call the base class's version that returns `''`, which then gets filtered out by the `if p` condition in the join.
Step 5 of 6
html.generate() runs the fixed algorithm using HTML-specific steps
`html.generate('Books', rows)` calls the inherited `generate`, which invokes `HtmlReport`'s `header`, `body` and `footer` in sequence, producing a full `
...
- ...
Step 6 of 6
md.generate() runs the exact same algorithm, producing Markdown instead
`md.generate('Books', rows)` goes through the identical `header → body → footer → filter → join` sequence in `generate`, but since `MarkdownReport` fills the hooks differently, the output is a `# Books` heading with `- ` list items and no closing footer line.
Click "Run" to execute this code in a sandboxed frame and see console output here.
TypeScript runs as plain JavaScript here - type annotations are stripped, not type-checked.
Click "Run" to execute this code in a sandboxed frame and see console output here.
Advantages
- Subclasses only need to fill in the steps that actually vary, instead of re-implementing the whole algorithm.
- The parts of the algorithm that never change live in one place, in the base class, instead of being copy-pasted across every subclass.
- Because the sequence of steps is fixed by the base class, subclasses cannot accidentally reorder or skip a stage of the algorithm.
Disadvantages
- Every new step added to the skeleton means another method every existing subclass may need to review or override.
- A subclass that overrides a hook carelessly can break an assumption the base algorithm relies on, since the base class can't enforce what the override actually does.
- Because the algorithm's shape is fixed through inheritance, adding a genuinely different variant usually means a new subclass rather than a runtime choice - Strategy is a better fit when that flexibility matters.
Question 1 of 10
What is the difference between an abstract step and a hook in Template Method?
Correct! Steps may either be abstract (forcing subclasses to provide an implementation) or have a default implementation that subclasses can optionally override (hooks).
Not quite. Steps may either be abstract (forcing subclasses to provide an implementation) or have a default implementation that subclasses can optionally override (hooks).
Question 2 of 10
What does Template Method fix in the base class, while leaving to subclasses?
Correct! Take the report generator: the base class fixes the sequence - title, content rows, footer - while HtmlReport and MarkdownReport each fill in their own markup for those steps. Add a timestamp section later, and it only needs to change in one place.
Not quite. Take the report generator: the base class fixes the sequence - title, content rows, footer - while HtmlReport and MarkdownReport each fill in their own markup for those steps. Add a timestamp section later, and it only needs to change in one place.
Question 3 of 10
What does Template Method guarantee about the algorithm's structure?
Correct! Subclasses can override specific steps without touching the template method itself, which guarantees the overall algorithm structure stays the same while individual steps remain customizable.
Not quite. Subclasses can override specific steps without touching the template method itself, which guarantees the overall algorithm structure stays the same while individual steps remain customizable.
Question 4 of 10
What is a ConcreteClass not allowed to do in Template Method?
Correct! ConcreteClass implements the abstract primitive operations to carry out the subclass-specific steps of the algorithm, but does not override the template method itself.
Not quite. ConcreteClass implements the abstract primitive operations to carry out the subclass-specific steps of the algorithm, but does not override the template method itself.
Question 5 of 10
What is a stated risk of a subclass overriding a hook carelessly?
Correct! A subclass that overrides a hook carelessly can break an assumption the base algorithm relies on, since the base class can't enforce what the override actually does.
Not quite. A subclass that overrides a hook carelessly can break an assumption the base algorithm relies on, since the base class can't enforce what the override actually does.
Question 6 of 10
What is the alternative approach of packing all logic into one class with conditionals, and what's wrong with it?
Correct! Packing all the logic into one class and using conditionals to choose the output format violates the Open/Closed Principle every time a new format is added.
Not quite. Packing all the logic into one class and using conditionals to choose the output format violates the Open/Closed Principle every time a new format is added.
Question 7 of 10
What goes wrong if the report-generation algorithm is copied entirely into each format's class (HTML, Markdown)?
Correct! Copying the entire algorithm into each report class means changing the overall flow, like adding a timestamp section, requires modifying every class individually.
Not quite. Copying the entire algorithm into each report class means changing the overall flow, like adding a timestamp section, requires modifying every class individually.
Question 8 of 10
When is Strategy said to be a better fit than Template Method?
Correct! Because the algorithm's shape is fixed through inheritance, adding a genuinely different variant usually means a new subclass rather than a runtime choice - Strategy is a better fit when that flexibility matters.
Not quite. Because the algorithm's shape is fixed through inheritance, adding a genuinely different variant usually means a new subclass rather than a runtime choice - Strategy is a better fit when that flexibility matters.
Question 9 of 10
In the reporting-tool example, what do the HTML and Markdown formats have in common?
Correct! Both formats follow the same overall structure - a title header, a list of content rows, and a footer - but the exact markup for each step differs, which is exactly what Template Method is built to share.
Not quite. Both formats follow the same overall structure - a title header, a list of content rows, and a footer - but the exact markup for each step differs, which is exactly what Template Method is built to share.
Question 10 of 10
According to the pros listed for Template Method, what do subclasses need to implement?
Correct! Subclasses only need to fill in the steps that actually vary, instead of re-implementing the whole algorithm.
Not quite. Subclasses only need to fill in the steps that actually vary, instead of re-implementing the whole algorithm.