Skip to main content
CleanCodeMastery

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.

23 min read Updated June 11, 2026beginner
template methoddesign patternsbehavioraltypescriptinheritancehooks

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:

  1. Boil water in the pan.
  2. Add tea leaves, crushed ginger, and elaichi, and let it boil.
  3. Pour into Dadi's steel tumbler.
  4. Add milk and two spoons of sugar.

For coffee she does this:

  1. Boil water.
  2. Pour the hot water over the coffee powder in the filter and let the decoction drip.
  3. Pour into Appa's dabarah set.
  4. 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:

Figure 1: Two orders, one skeleton — only the brew and extras moments differ

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 subclassesMasalaChai, 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 GreenTea class from pouring before brewing. The skeleton is a convention, not a rule.
Figure 2: Duplicated skeletons drift apart; one template keeps every variant in step

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:

Figure 3: Lines you must write per new drink — copies repeat the skeleton, the template charges only for the 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:

  1. Break the algorithm into named steps. Boil, brew, pour, add extras. If you cannot find a fixed sequence, the pattern does not apply.
  2. 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.
  3. 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).
  4. Implement the shared steps in the base class — once, for everyone.
  5. Declare the varying steps abstract so the compiler forces every subclass to fill them.
  6. Add hooks where useful. Empty-body hooks (beforeServing()) let subclasses inject extra behaviour; boolean hooks (wantsExtras()) let them switch optional parts on or off.
  7. Write the subclasses. Each fills in only its blanks. None touches the template.
Figure 4: Structure — the base class owns the recipe; subclasses fill the marked slots

Keep this difference table pinned in your memory — it is the most asked detail of the pattern:

Kind of stepWhere is the body?Must subclass implement?Example
Shared stepBase class, full bodyNo — inherited as-isboilWater()
Abstract stepNowhere — declaration only✅ Yes, compulsorybrew()
Hook (extension)Base class, empty bodyOptionalbeforeServing()
Hook (flow control)Base class, returns defaultOptionalwantsExtras(): true

And here is the recipe itself as a flow — the fixed spine with the hook as the only fork:

Figure 5: The template method as a flow — fixed spine, two filled blanks, one optional hook gate

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 returned false, 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:

Figure 6: Do not call us, we will call you — the base class runs the show and dips into the subclass at the blanks

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:

Figure 7: Every drink walks the same line of stations — the template guarantees this order for all subclasses

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 Activity runs a fixed lifecycle (onCreate → onStart → onResume → ...) and your app fills in the steps. ASP.NET page/middleware lifecycles, game-engine loops (Awake/Start/Update in 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.InputStream implements the multi-byte read(byte[], off, len) in terms of an abstract single-byte read() that subclasses supply. AbstractList, AbstractMap, and friends define whole collection behaviour around a few abstract primitives you implement.
  • Spring Framework templates. JdbcTemplate and 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 parse and validate. 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:

Figure 8: Where developers meet Template Method in the wild — mostly inside frameworks that call them

When to use it and when not to 🤔

SituationUse 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?

Figure 9: Template Method or Strategy? Grain of variation versus moment of choice

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:

  1. 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.
  2. 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.
  3. 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.
  4. Calling steps from outside the template. If client code starts calling drink.brew() directly, the carefully fixed order is bypassed. Keep step methods protected so only the recipe can run them.
  5. Forcing the pattern when the order varies. If GreenTea needs 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 👯

PatternVaries what?MechanismDecided when?Typical home
Template MethodIndividual steps of one algorithmInheritance — subclass overridesCompile time (by choosing the subclass)Frameworks, importers, test runners
StrategyThe whole algorithmComposition — context holds an objectRuntime (swap the object)Payment modes, comparators
Factory MethodJust the object-creation stepInheritanceCompile timeInside templates, very often
BuilderConstruction steps via a directorCompositionRuntimeMulti-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.

Figure 10: Template Method and its relatives — inheritance fixes the skeleton, composition swaps the whole

The whole pattern on one page 🗺️

If you can redraw this mind map from memory, you own the pattern:

Figure 11: The Template Method pattern, mapped — skeleton, rule, hooks, and famous homes

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:

  1. Morning assembly template. Every school assembly follows: line up → prayer → special item (varies) → announcements → disperse. Write an abstract Assembly class with that template, then MondayAssembly (special item: pledge), FridayAssembly (special item: student talent show), and a hasAnnouncements() hook that SaturdayAssembly overrides to false. Print the full flow for each day.
  2. Exam paper generator. An abstract ExamPaper runs: print header → print instructions → print questions (abstract) → print marks table. Create MathsPaper and HistoryPaper. Then add a hook needsGraphSheet() that only MathsPaper turns on, making the template print "Attach graph sheet" at the right moment.
  3. 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?
  4. College stretch. Take your Assembly template from exercise 1 and rebuild it without inheritance: a single runAssembly(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