Template Method Pattern: Chai and Coffee, Same Steps
Learn the Template Method design pattern with a chai and coffee story, easy TypeScript and C# code, hooks, diagrams, real examples, and practice tasks.
Amma's kitchen: chai for Dadi, coffee for Appa ☕
Early morning in a Bengaluru home. Amma is in the kitchen, and two orders are waiting: Dadi wants her masala chai, and Appa wants his filter coffee. Twelve-year-old Anjali sits on the kitchen stool, watching, because her science homework says "observe a process and write its steps".
Watch Amma carefully. For chai she does this:
- Boil water in the pan.
- Add tea leaves, crushed ginger, and elaichi, and let it boil.
- Pour into Dadi's steel tumbler.
- Add milk and two spoons of sugar.
For coffee she does this:
- Boil water.
- Pour the hot water over the coffee powder in the filter and let the decoction drip.
- Pour into Appa's dabarah set.
- Add hot frothy milk and one spoon of sugar.
Anjali writes both lists in her notebook, side by side, and suddenly sees it. Steps 1 and 3 are exactly the same. Steps 2 and 4 are different, but they sit in the same position in both recipes. Amma never pours before boiling. She never adds milk before brewing. The order is fixed; only the contents of two slots change.
"Amma," she says, "you are not making two drinks. You are running one recipe with two blanks."
If Amma wrote her morning routine as a recipe card, it would say:
Boil water → brew (your way) → pour into cup → add extras (your way)
One card, two drinks. Tomorrow if Bhaiya asks for green tea, the same card works — only the "brew" slot changes again. And when Uncle visits and demands strict black coffee with nothing added, the card still works — the "extras" slot is simply skipped.
This recipe card is the Template Method pattern. The fixed card is the template. The blank slots are the steps that subclasses fill in. Keep Amma's kitchen in your mind for the whole post; we will code it line by line.
Here is Anjali's observation sheet, drawn as the morning's journey:
What is the Template Method pattern? 🧠
Template Method is a behavioral design pattern built on inheritance. Here is the plain definition: a base class writes the skeleton of an algorithm inside one method — the template method — as a fixed sequence of step calls. Some steps are implemented right there in the base class because they never change (boil water). Other steps are declared abstract, and every subclass must supply them (brew). A third kind, called hooks, have a default body and subclasses may override them (do you even want extras?).
The crucial promise: subclasses can change WHAT a step does, but never WHEN it happens. The order belongs to the base class alone. Good implementations even mark the template method final (or simply never override it) so nobody can shuffle the steps.
This flips the usual direction of control. Normally your code calls library code. Here, the base class calls down into the subclass at the right moments — "now tell me how YOU brew". This is so common in frameworks that it has a nickname: the Hollywood Principle — "Don't call us, we'll call you."
One-line summary: Template Method = the parent writes the full recipe in stone, and leaves a few labelled blanks for each child to fill in its own way.
The cast:
- Abstract class — owns the template method (
prepare()), the shared steps (boilWater), the abstract steps (brew), and the hooks (wantsExtras). - Concrete subclasses —
MasalaChai,FilterCoffee— implement the abstract steps, maybe override a hook, and never touch the template.
College corner — the Hollywood Principle, properly: what you are seeing is inversion of control (IoC), the idea that separates a framework from a library. With a library, your code holds the main flow and calls the library when it needs help. With a framework, the framework holds the main flow and calls your code at fixed extension points. Template Method is IoC in miniature: prepare() is the framework's main flow; brew() is your extension point. When you later meet big IoC — Spring calling your beans, JUnit calling your setUp, Android calling your onCreate — recognise it as the same kitchen, scaled up. The base class is Amma; you only get to say how the brewing happens, never when.
The problem it solves 😵
Suppose Anjali codes the kitchen without the pattern. Two separate classes, each with its own full recipe:
// BAD CODE: two classes, one copied skeleton
class MasalaChai {
prepare(): void {
console.log("Boiling water to 100 degrees"); // copy 1
console.log("Boiling tea leaves, ginger and elaichi");
console.log("Pouring into the cup"); // copy 1
console.log("Adding milk and 2 spoons sugar");
}
}
class FilterCoffee {
prepare(): void {
console.log("Boiling water to 100 degrees"); // copy 2
console.log("Dripping decoction through the filter");
console.log("Pouring into the cup"); // copy 2
console.log("Adding hot frothy milk and 1 spoon sugar");
}
}Only four lines each, and half of them are duplicates. Now imagine real importers, report generators, or game levels with thirty-line skeletons. The dangers grow quietly:
- Fix once, forget twice. A bug in the shared part ("water should be 95 degrees, not 100, for green tea safety") must be fixed in every copy. Miss one class and the recipes silently disagree.
- New requirements multiply the pain. "Wash the pan before boiling" — now thread that line through chai, coffee, green tea, badam milk… every class, same edit, ten times.
- The shared shape exists nowhere. No single place in the code says "every hot drink follows boil → brew → pour → extras." A new programmer must reverse-engineer the recipe from one copy and hope the others agree.
- Order mistakes creep in. Nothing stops a new
GreenTeaclass from pouring before brewing. The skeleton is a convention, not a rule.
Count the duplicated lines as the drink menu grows. With copies, every new drink re-pastes the whole skeleton. With the template, every new drink writes only its two blanks:
The structure is shared. The steps vary. Template Method makes the code say that distinction out loud.
How it works, step by step 🛠️
The recipe for writing recipes:
- Break the algorithm into named steps. Boil, brew, pour, add extras. If you cannot find a fixed sequence, the pattern does not apply.
- Sort the steps into three buckets. Same for everyone → concrete method on the base class. Different for everyone → abstract method. Optional or has a sensible default → hook.
- Write the template method on the abstract base class. It calls the steps in the fixed order. Make it final (or by team rule, never overridden).
- Implement the shared steps in the base class — once, for everyone.
- Declare the varying steps abstract so the compiler forces every subclass to fill them.
- Add hooks where useful. Empty-body hooks (
beforeServing()) let subclasses inject extra behaviour; boolean hooks (wantsExtras()) let them switch optional parts on or off. - Write the subclasses. Each fills in only its blanks. None touches the template.
Keep this difference table pinned in your memory — it is the most asked detail of the pattern:
| Kind of step | Where is the body? | Must subclass implement? | Example |
|---|---|---|---|
| Shared step | Base class, full body | No — inherited as-is | boilWater() |
| Abstract step | Nowhere — declaration only | ✅ Yes, compulsory | brew() |
| Hook (extension) | Base class, empty body | Optional | beforeServing() |
| Hook (flow control) | Base class, returns default | Optional | wantsExtras(): true |
And here is the recipe itself as a flow — the fixed spine with the hook as the only fork:
Real-life code example 💻
Amma's kitchen, complete and runnable, with two subclasses sharing one skeleton (plus a third to show off hooks):
// ---------- The abstract base class ----------
abstract class HotDrink {
// THE TEMPLATE METHOD.
// Fixed order. Subclasses never override this.
prepare(): void {
this.boilWater();
this.brew(); // blank slot 1 — compulsory
this.pourIntoCup();
if (this.wantsExtras()) { // hook controls the optional part
this.addExtras(); // blank slot 2 — compulsory
}
this.serve();
}
// ----- shared steps: written ONCE for every drink -----
protected boilWater(): void {
console.log("Boiling water in the pan");
}
protected pourIntoCup(): void {
console.log("Pouring into the cup");
}
protected serve(): void {
console.log("Ready! Serve hot.\n");
}
// ----- abstract steps: every subclass MUST fill these -----
protected abstract brew(): void;
protected abstract addExtras(): void;
// ----- hook: subclasses MAY override; default says yes -----
protected wantsExtras(): boolean {
return true;
}
}
// ---------- Subclass 1: Dadi's chai ----------
class MasalaChai extends HotDrink {
protected brew(): void {
console.log("Boiling tea leaves with ginger and elaichi");
}
protected addExtras(): void {
console.log("Adding milk and 2 spoons of sugar");
}
}
// ---------- Subclass 2: Appa's filter coffee ----------
class FilterCoffee extends HotDrink {
protected brew(): void {
console.log("Dripping decoction through the brass filter");
}
protected addExtras(): void {
console.log("Adding hot frothy milk and 1 spoon of sugar");
}
}
// ---------- Subclass 3: Uncle's strict black coffee ----------
class BlackCoffee extends HotDrink {
protected brew(): void {
console.log("Brewing strong black coffee");
}
protected addExtras(): void {
/* never called — see the hook below */
}
protected wantsExtras(): boolean {
return false; // hook overridden: skip extras entirely
}
}
// ---------- The morning run ----------
console.log("--- Dadi's order ---");
new MasalaChai().prepare();
console.log("--- Appa's order ---");
new FilterCoffee().prepare();
console.log("--- Uncle's order ---");
new BlackCoffee().prepare();The output:
--- Dadi's order ---
Boiling water in the pan
Boiling tea leaves with ginger and elaichi
Pouring into the cup
Adding milk and 2 spoons of sugar
Ready! Serve hot.
--- Appa's order ---
Boiling water in the pan
Dripping decoction through the brass filter
Pouring into the cup
Adding hot frothy milk and 1 spoon of sugar
Ready! Serve hot.
--- Uncle's order ---
Boiling water in the pan
Brewing strong black coffee
Pouring into the cup
Ready! Serve hot.Read the output like a teacher checks homework:
- Lines 1 and 3 of every drink are identical — they come from the base class, written once.
- The brew line and the extras line are different per drink — supplied by the subclasses.
- Uncle's black coffee skipped the extras line completely — the
wantsExtras()hook returnedfalse, and the template politely jumped over that step. - The client only ever called
prepare(). It does not know which steps were shared, overridden, or skipped. The recipe is one method, readable top to bottom, in one place.
Now watch the Hollywood Principle in action, traced call by call. Anjali calls prepare() on the chai object — and then the base class takes over and calls down into the subclass at exactly the right moments:
And when the new requirement arrives — "log the gas usage after boiling" — you add one line inside boilWater() in the base class, and chai, coffee, and black coffee all get it instantly. Fix once, fixed everywhere.
One more view of the same run. Every drink, regardless of type, marches through the same stations in the same order — only what happens inside two stations differs:
The same idea in C# 🗂️
Appa works at an office where the same pattern appears in grown-up clothes: exporting a report. Every exporter fetches data, formats it (the varying step), and saves the file.
abstract class ReportExporter
{
// The template method — sealed-by-convention, fixed order.
public void Export()
{
var rows = FetchRows(); // shared
string content = Format(rows); // varies — abstract
Save(content); // shared
AfterExport(); // hook — default does nothing
}
protected List<string> FetchRows() =>
new() { "Asha,92", "Bilal,88", "Chitra,95" };
protected void Save(string content) =>
Console.WriteLine($"Saving file:\n{content}\n");
protected abstract string Format(List<string> rows);
protected virtual void AfterExport() { } // empty hook
}
class CsvExporter : ReportExporter
{
protected override string Format(List<string> rows) =>
"name,marks\n" + string.Join("\n", rows);
}
class HtmlExporter : ReportExporter
{
protected override string Format(List<string> rows) =>
"<table>" + string.Concat(
rows.Select(r => $"<tr><td>{r.Replace(",", "</td><td>")}</td></tr>"))
+ "</table>";
protected override void AfterExport() =>
Console.WriteLine("Opening preview in browser...");
}
// Usage — same call, two skeleton-sharing exporters
new CsvExporter().Export();
new HtmlExporter().Export();The fetch and save logic exists once. Each exporter contributes only its Format, and HtmlExporter additionally uses the AfterExport hook. Notice how natural the hook feels: CsvExporter did not even mention it.
And once more in Python 🐍
Python spells the same idea with abc. Here is a tiny file importer skeleton — open, parse (the blank), validate (a hook), save:
from abc import ABC, abstractmethod
class FileImporter(ABC):
def run(self, path): # THE TEMPLATE METHOD
data = self.open_file(path) # shared
rows = self.parse(data) # blank slot — compulsory
if self.should_validate(): # hook — default yes
rows = [r for r in rows if r]
self.save(rows) # shared
def open_file(self, path):
print(f"Opening {path}")
return "a,b;;c,d" # pretend file content
def save(self, rows):
print(f"Saved {len(rows)} rows")
def should_validate(self): # hook with default
return True
@abstractmethod
def parse(self, data): ...
class CsvImporter(FileImporter):
def parse(self, data):
return data.split(";") # naive csv blocks
class TrustedImporter(FileImporter):
def parse(self, data):
return data.split(";")
def should_validate(self): # hook overridden
return False # skip cleaning, trust the source
CsvImporter().run("marks.csv") # Saved 2 rows (empty block removed)
TrustedImporter().run("marks.csv") # Saved 3 rows (validation skipped)Same skeleton, three languages. The pattern is the shape, not the syntax.
Where you see it in real software 🌍
Template Method quietly runs huge parts of the software world:
- Testing frameworks (the classic). JUnit, NUnit, pytest, MSTest all run your tests inside a fixed lifecycle: setUp → run the test → tearDown. The framework owns the skeleton; you fill the slots by writing
setUp()/tearDown()(or[SetUp]/[TearDown], fixtures, etc.). You never control when they run — the framework calls you. Pure Hollywood Principle. - Framework lifecycle hooks everywhere. An Android
Activityruns a fixed lifecycle (onCreate → onStart → onResume → ...) and your app fills in the steps. ASP.NET page/middleware lifecycles, game-engine loops (Awake/Start/Updatein Unity), and React class-component lifecycle methods all follow the same shape: the framework's skeleton calls your overridden steps. - Java's standard library.
java.io.InputStreamimplements the multi-byteread(byte[], off, len)in terms of an abstract single-byteread()that subclasses supply.AbstractList,AbstractMap, and friends define whole collection behaviour around a few abstract primitives you implement. - Spring Framework templates.
JdbcTemplateand its cousins fix the boring skeleton (open connection, handle errors, close connection) and let you supply only the interesting step (the query and row mapping). The Spring docs openly credit this pattern family. - Data pipelines and importers. Real ETL code bases define an abstract importer — open, parse, validate, transform, write, report — where each file format overrides only
parseandvalidate. Our Python example is a baby version of exactly this. - Open-source code to read. The iluwatar template-method example is a short, well-commented Java sample, and refactoring.guru's page walks the same idea in many languages.
If you tally where working developers actually meet this pattern (usually without writing it themselves), the picture looks like this:
When to use it and when not to 🤔
| Situation | Use Template Method? |
|---|---|
| Several classes do the same steps in the same order with small differences | ✅ Yes — lift the skeleton into a base class |
| You must guarantee an order/invariant ("always close the file at the end") | ✅ Yes — the final template enforces it |
| You are writing a framework and want users to plug into fixed slots | ✅ Yes — this is the framework pattern |
| Most of the algorithm is shared, only 1–3 steps vary | ✅ Yes — the sweet spot |
| Variants need a different order of steps | ❌ No — the fixed skeleton cannot bend |
| Behaviour must be swapped at runtime | ❌ No — use Strategy (composition) |
| Nothing is genuinely shared between the variants | ❌ No — a common base would be a lie |
| Your language/team avoids inheritance | ❌ Prefer composition: pass step functions instead |
When you are torn between Template Method and Strategy, place your problem on this map. The two questions: how much of the algorithm varies, and when must the variation be chosen?
Chai vs coffee sits deep in Template Method country: tiny variation (two slots), fixed at the moment you choose which class to instantiate. Payment modes sit in Strategy country: the whole flow differs and the user picks at runtime.
Common mistakes students make 🚧
The number one mistake: letting subclasses override the template method itself. The moment MasalaChai redefines prepare(), the guarantee is gone — it can skip boiling or pour first, and the "one authoritative recipe" promise collapses. Mark the template final/sealed where the language allows, or enforce it firmly in code review.
More traps to avoid:
- Too many abstract steps. If your base class declares eight abstract methods, every subclass must write eight bodies — even trivial ones. That punishes subclasses with boilerplate. Keep compulsory steps minimal; turn the rest into hooks with defaults.
- Hooks that secretly became compulsory. If every single subclass overrides a "hook", it was never optional — promote it to an abstract step so the compiler enforces it.
- Breaking the base class's trust (Liskov violations). The template trusts its steps. A
brew()that throws an exception, returns garbage, or secretly pours the cup corrupts the whole recipe from inside. A step should do its slot's job — nothing more. - Calling steps from outside the template. If client code starts calling
drink.brew()directly, the carefully fixed order is bypassed. Keep step methodsprotectedso only the recipe can run them. - Forcing the pattern when the order varies. If
GreenTeaneeds pour-then-brew, do not contort the template with flags and double hooks. The pattern's whole value is the fixed order; when the order itself varies, choose Strategy or plain composition.
A naming habit that helps readers: name the template method after the whole job (prepare, export, runTest) and the steps after the slot (brew, format, setUp). Anyone reading the template then sees the entire algorithm as a tidy table of contents.
Compare with cousins 👯
| Pattern | Varies what? | Mechanism | Decided when? | Typical home |
|---|---|---|---|---|
| Template Method | Individual steps of one algorithm | Inheritance — subclass overrides | Compile time (by choosing the subclass) | Frameworks, importers, test runners |
| Strategy | The whole algorithm | Composition — context holds an object | Runtime (swap the object) | Payment modes, comparators |
| Factory Method | Just the object-creation step | Inheritance | Compile time | Inside templates, very often |
| Builder | Construction steps via a director | Composition | Runtime | Multi-part object assembly |
The headline comparison is Template Method vs Strategy — the classic inheritance vs composition duel:
- Template Method says: "Inherit from me. I fix the skeleton; you override the marked steps." The variation is chosen at compile time — once an object is a
FilterCoffee, it brews like one forever. The grain of variation is per step. - Strategy says: "Hold a reference to me. Swap me whenever you like." The variation is chosen at runtime, and the grain is the whole algorithm. No inheritance chains, no single-base-class limit.
Rough guide: shared fixed sequence with small overridable slots → Template Method. Need to switch behaviour mid-program, or want to avoid inheritance → Strategy. The two even cooperate: a template method's step can delegate to a strategy object.
College corner: one more relation worth remembering for exams: Factory Method is a specialized Template Method. The "create the object" step inside a bigger template is itself a factory method — the subclass decides which product the recipe works with. And a design-review question worth asking yourself: since Template Method couples the variant to the base class forever (inheritance is the strongest coupling there is), modern codebases often refactor mature templates into Strategy form — the skeleton becomes a plain function that receives its steps as parameters. Functional programmers call that shape a higher-order function; the GoF would call it Template Method rebuilt with composition. Knowing both spellings, and when each earns its keep, is the actual skill.
The whole pattern on one page 🗺️
If you can redraw this mind map from memory, you own the pattern:
Quick revision box 📦
+--------------------------------------------------------------+
| TEMPLATE METHOD PATTERN — REVISION |
+--------------------------------------------------------------+
| WHAT : Base class fixes the algorithm skeleton in ONE |
| method; subclasses fill marked step slots |
| ACTORS : Abstract class (template + steps + hooks) |
| + concrete subclasses |
| STEPS : shared (base body) | abstract (must fill) |
| | hook (may override, has default) |
| KEY RULE : Subclass changes WHAT a step does, never WHEN |
| NICKNAME : Hollywood Principle — don't call us, we call you |
| KILLS : Copy-pasted skeletons drifting apart |
| AVOID IF : Order of steps differs per variant, or you need |
| runtime swapping (then: Strategy) |
| EXAMPLES : JUnit setUp/tearDown, Android lifecycle, |
| InputStream.read, JdbcTemplate, importers |
+--------------------------------------------------------------+Practice exercise ✍️
Cook these yourself — the pattern only sticks when you type it:
- Morning assembly template. Every school assembly follows: line up → prayer → special item (varies) → announcements → disperse. Write an abstract
Assemblyclass with that template, thenMondayAssembly(special item: pledge),FridayAssembly(special item: student talent show), and ahasAnnouncements()hook thatSaturdayAssemblyoverrides tofalse. Print the full flow for each day. - Exam paper generator. An abstract
ExamPaperruns: print header → print instructions → print questions (abstract) → print marks table. CreateMathsPaperandHistoryPaper. Then add a hookneedsGraphSheet()that onlyMathsPaperturns on, making the template print "Attach graph sheet" at the right moment. - Refactor hunt (thinking task). Open any project you have written with two classes that look like cousins — two parsers, two screens, two report writers. List their methods side by side and underline the identical lines. If more than half match, sketch (on paper) the abstract base class: which lines become the template, which become abstract steps, and which deserve hooks?
- College stretch. Take your
Assemblytemplate from exercise 1 and rebuild it without inheritance: a singlerunAssembly(specialItem, hasAnnouncements)function that receives the varying step as a function argument. Compare the two designs in three sentences: which is easier to test, which is easier to discover, and which would survive a requirement that the order itself changes on Sports Day?
Frequently asked questions
- What is the Template Method pattern in one line?
- A base class writes the full recipe of an algorithm in one method, in a fixed order, and leaves a few steps blank. Subclasses fill in only those blank steps; they can never reorder or skip the recipe itself.
- What is the difference between an abstract step and a hook?
- An abstract step MUST be implemented by every subclass — the recipe cannot run without it, like the brew step. A hook MAY be overridden — it already has a default body, often empty, like an optional add-toppings step. Abstract = compulsory homework; hook = optional bonus question.
- What is the Hollywood Principle?
- It means 'don't call us, we'll call you'. The subclass does not run the show. The base class runs the recipe and calls down into the subclass at the right moments. The subclass only supplies behaviour; it never controls the order.
- How is Template Method different from Strategy?
- Template Method varies STEPS of one algorithm using inheritance, fixed at compile time. Strategy swaps the WHOLE algorithm using composition, changeable at runtime. If you need to switch behaviour while the program runs, prefer Strategy.
- When should I avoid Template Method?
- Avoid it when variants need a different ORDER of steps — the fixed skeleton cannot bend. Also avoid it when there is nothing truly shared between variants, or when your language style prefers composition and plain functions over inheritance.
Further reading
Related Lessons
Strategy Pattern: Cycle, Bus, or Auto — You Choose
Learn the Strategy design pattern with a simple school travel story, easy TypeScript and C# code, runtime swapping, real examples, and practice tasks.
State Pattern: The Fan That Changes Its Mood
Learn the State design pattern with a ceiling fan regulator story, simple TypeScript and C# code, state diagrams, real software examples, and practice.
Factory Method Pattern: Let the Branch Decide the Vehicle
Learn the Factory Method design pattern with a simple Indian tiffin service story, easy TypeScript and C# code, diagrams, real examples, and practice.
Visitor Pattern: The Doctor Who Visits Every Class
Learn the Visitor design pattern with a school health check-up story, double dispatch made simple, TypeScript and C# code, real examples, and practice.