Form Template Method: One Recipe Card, Many Biryanis
Learn the Form Template Method refactoring with a two-aunties biryani story, step-by-step extraction of a shared algorithm skeleton in TypeScript and Python, and how this refactoring produces the Template Method design pattern.
🍚 Two aunties, one biryani
In a Hyderabad apartment building live two famous cooks. Shabnam Aunty on the second floor makes a chicken biryani that people remember for weeks. Meena Aunty on the fourth floor is strictly vegetarian, and her veg biryani has its own fan club. The two aunties are friendly rivals — at every building function, plates from both kitchens circulate, and loyalties are tested.
One Sunday, the building decided to print a community recipe book. Priya, the engineering student from the sixth floor, volunteered to type the recipes. Both aunties dictated their biryani recipes to her, one after the other, and halfway through the second one Priya started smiling. Shabnam Aunty's recipe said:
- Soak the basmati rice for thirty minutes.
- Fry the onions and whole masala in ghee.
- Add the marinated chicken and cook until tender.
- Layer the part-cooked rice over the masala.
- Seal the pot and cook on dum for twenty minutes.
Meena Aunty's recipe said:
- Soak the basmati rice for thirty minutes.
- Fry the onions and whole masala in ghee.
- Add the paneer and vegetables and cook until soft.
- Layer the part-cooked rice over the masala.
- Seal the pot and cook on dum for twenty minutes.
Five steps each. Four of them — word for word — identical. Only step 3 differs. Priya did the smart thing: instead of printing two full recipes, she printed one recipe card titled "Biryani," with steps 1, 2, 4, and 5 written once, and step 3 shown as a fill-in-the-blank: "Add your main ingredient and cook it through — chicken (Shabnam style) or paneer and vegetables (Meena style)."
The aunties protested for exactly one minute — "my recipe is special!" — until Priya pointed at the card and asked, "Aunty, which of these four steps is different in your kitchen?" Silence. The specialness lived entirely in step 3, and the card said so honestly.
Now look at what that one card buys the whole building. If the dum time changes from twenty minutes to twenty-five — Shabnam Aunty has been experimenting — it is corrected in one place and every biryani in the building improves together. If tomorrow Fatima Aunty from the third floor invents an egg biryani, she does not write a whole new recipe — she only fills in the blank. And nobody's recipe can quietly drift out of order, because the order lives on the shared card. Last year, before the card, Meena Aunty's niece copied the recipe by hand and swapped steps 2 and 4 — the building still talks about that biryani. With one card, that mistake is structurally impossible.
That recipe card is today's refactoring. When two subclasses perform the same sequence of steps and differ only in some step's details, we lift the shared sequence into one superclass method and leave the differing steps as blanks the subclasses fill in. This is Form Template Method.
What is Form Template Method? 📋
Form Template Method attacks the sneakiest kind of duplication: duplication of shape. Two subclass methods follow the same procedure — the same steps, in the same order — but the lines themselves differ in detail. A line-by-line diff sees two different methods. Your eyes, reading both, see one algorithm written twice.
This duplication is dangerous precisely because it is invisible to tools. And like all duplicated knowledge, it drifts. One subclass adds a validation step; the other does not get it. Someone reorders two steps in one copy "because it reads better." Six months later, the two procedures that were "the same" disagree in three subtle ways, and nobody can say which differences are intentional.
How much of the typical pair of "different" methods is actually shared? Priya measured her two recipes, and the answer is the usual one:
The cure has two movements:
- Make the skeletons identical. Inside each subclass, extract the differing parts into small methods with the same name and signature in both subclasses. After enough extraction, the two top-level methods become textually identical — every line is either shared logic or a call to a same-named step.
- Lift the skeleton. Pull the now-identical top-level method up into the superclass. The differing steps stay behind in the subclasses, declared as abstract (or overridable) operations on the parent. The lifted method is called the template method: it owns the order; the subclasses own the details.
The result is a structure with a famous name. Form Template Method is the refactoring that produces the Template Method pattern — the Gang of Four pattern whose definition is exactly our recipe card: define the skeleton of an algorithm in an operation, deferring some steps to subclasses. The pattern is the destination; this refactoring is the road that starts from existing duplicated code instead of from an up-front design.
One-line summary: when two subclasses run the same steps in the same order with different details, extract the differing steps into same-named methods, pull the shared sequence into a superclass template method, and let each subclass fill in only its own blanks.
One more vocabulary word, because it earns its place: a hook. Not every step must be abstract. A step that only some subclasses want to change can be given a default implementation in the parent — often an empty body. Subclasses may override it but are not forced to. On the recipe card: "Step 4½ (optional): add fried cashews if you like." Shabnam Aunty skips it; Meena Aunty fills it in. Hooks keep the template flexible without forcing every cook to answer every question.
When do we need it? 🔍
The signs to watch for:
- Parallel methods in sibling subclasses. Two (or more) subclasses each implement a method like
generate(),process(), orexport(), and reading them side by side feels like reading the same paragraph with a few words swapped. That is Duplicate Code at the level of structure — the worst kind, because diff tools cannot see it. - A bug fixed twice. The history shows the same fix applied to
HtmlReport.renderin March and toMarkdownReport.renderin May, when someone finally noticed the second copy. Knowledge written twice is knowledge that drifts. - A new variant means copy-paste. Adding
PdfReportstarts with "copyHtmlReport, change three lines." Every copy deepens the hole. - The differences hide the sameness. Code reviewers cannot quickly answer "what is actually different between these two subclasses?" because the genuine differences are buried inside two hundred shared lines.
And the situations where it does not apply:
- The order genuinely differs. If one subclass validates-then-saves and the other saves-then-validates on purpose, there is no shared skeleton to lift. Forcing one distorts both procedures. The steps must really share an order.
- The resemblance is superficial. Two methods that merely both "loop and build a string" are not the same algorithm. Apply the test: could you write one numbered recipe that honestly describes both? If not, leave them apart.
- The variation must change at runtime. A template method welds the variation into the class hierarchy at compile time. If a live object needs to swap a step — or combine steps freely — you want a Strategy object instead, reached via Replace Inheritance with Delegation.
- The classes are not yet siblings. If the duplicated methods live in unrelated classes, first give them a common parent with Extract Superclass; only then can a template method exist. And if a subclass would refuse most of what the parent offers, you are heading toward Refused Bequest — reconsider whether inheritance is the right home at all. A subclass so thin it adds nothing is the opposite problem, Lazy Class, with its own cure.
Here is the apply-or-not decision condensed into one table:
| Question | Yes → | No → |
|---|---|---|
| Same steps in the same order in every variant? | Continue | Stop — no shared skeleton exists |
| Can one honest numbered recipe describe all variants? | Continue | Stop — resemblance is superficial |
| Is the variation fixed per class (not per call)? | Continue | Use Strategy via delegation instead |
| Are the classes siblings (or can they honestly become siblings)? | Apply Form Template Method | Extract Superclass first, then re-check |
And the choice between a template method and its closest rival, the Strategy pattern, mapped on two axes — how stable the step order is, and whether variation must move at runtime:
Before and after at a glance
Let us put the two aunties in code. Before — two cook methods, four-fifths identical:
// BEFORE: the same procedure written twice; only one step truly differs
class ChickenBiryaniCook {
cook(): string[] {
const log: string[] = [];
log.push("Soak basmati rice 30 min"); // shared
log.push("Fry onions and whole masala in ghee"); // shared
log.push("Add marinated chicken, cook till tender"); // DIFFERENT
log.push("Layer part-cooked rice over masala"); // shared
log.push("Seal pot, dum for 20 min"); // shared
return log;
}
}
class VegBiryaniCook {
cook(): string[] {
const log: string[] = [];
log.push("Soak basmati rice 30 min"); // shared (copy #2)
log.push("Fry onions and whole masala in ghee"); // shared (copy #2)
log.push("Add paneer and vegetables, cook till soft"); // DIFFERENT
log.push("Layer part-cooked rice over masala"); // shared (copy #2)
log.push("Seal pot, dum for 20 min"); // shared (copy #2)
return log;
}
}After — one recipe card in the parent, one filled-in blank per aunty:
// AFTER: the skeleton lives once; subclasses supply only their difference
abstract class BiryaniCook {
cook(): string[] { // the TEMPLATE METHOD
const log: string[] = [];
log.push("Soak basmati rice 30 min");
log.push("Fry onions and whole masala in ghee");
log.push(this.addMainIngredient()); // the blank to fill in
log.push("Layer part-cooked rice over masala");
log.push(this.garnish()); // a HOOK - optional
log.push("Seal pot, dum for 20 min");
return log;
}
protected abstract addMainIngredient(): string; // every cook must answer
protected garnish(): string { return "No garnish"; } // default: skip
}
class ChickenBiryaniCook extends BiryaniCook {
protected addMainIngredient(): string {
return "Add marinated chicken, cook till tender";
}
}
class VegBiryaniCook extends BiryaniCook {
protected addMainIngredient(): string {
return "Add paneer and vegetables, cook till soft";
}
protected garnish(): string { // only Meena Aunty overrides the hook
return "Add fried cashews";
}
}Count what each subclass contains now: only its genuine difference. Adding Fatima Aunty's egg biryani is a three-line class. Changing the dum time edits one line, once, for everyone. And the order of steps physically cannot drift between variants, because there is only one copy of the order in existence.
Watch the conversation between the template and a subclass while one biryani cooks. The parent runs the show; the child only answers when asked:
College corner: this upside-down control flow has a famous nickname — the Hollywood principle: "don't call us, we'll call you." In ordinary code, the specific thing calls the general thing (your function calls a library). In a template method, the general thing calls the specific thing: the abstract parent drives, and concrete subclasses are called by it at the blanks. This inversion of control is the same idea that powers frameworks everywhere — you do not call React or ASP.NET; they call your components and handlers at well-defined points. When you form a template method, you are building a tiny framework with named extension points.
Step-by-step, the safe way 🪜
The refactoring is a sequence of tiny, individually-safe moves. The golden rule: the two methods must become textually identical before anything is pulled up.
Step 1: Line up the two methods side by side. Mark every line as either shared (identical in both) or varying. If you cannot produce such a marking — if the orders disagree — stop here; this refactoring does not apply.
Step 2: Extract each varying part into a method — with the same name in both subclasses. This is the heart of the technique. Apply Extract Method inside each subclass so that the differing lines move into small methods whose names and signatures match exactly across the siblings:
// Intermediate state: both cook() methods now read IDENTICALLY,
// but each still lives in its own subclass. Nothing pulled up yet.
class ChickenBiryaniCook {
cook(): string[] {
const log: string[] = [];
log.push("Soak basmati rice 30 min");
log.push("Fry onions and whole masala in ghee");
log.push(this.addMainIngredient()); // extracted, same name in both
log.push("Layer part-cooked rice over masala");
log.push("Seal pot, dum for 20 min");
return log;
}
private addMainIngredient(): string {
return "Add marinated chicken, cook till tender";
}
}
// VegBiryaniCook.cook() is now line-for-line THE SAME — only
// its addMainIngredient() body differs.Compile and test after each extraction, not after all of them.
Step 3: Verify the skeletons match. Diff the two cook() bodies. They must be character-for-character identical. If a stray difference remains — a different variable name, an extra log line — either make it shared or extract it too.
Step 4: Pull the skeleton up. Apply Pull Up Method (creating the superclass first with Extract Superclass if none exists). The lifted cook() is now the template method. Delete the identical copies from both subclasses.
Step 5: Declare the steps on the parent. Each extracted step method becomes protected abstract on the superclass; the subclass versions become its implementations. The compiler now enforces that every new variant answers every required question.
Step 6: Convert optional steps into hooks. Where only some subclasses need a step, give the parent a default (often empty) implementation instead of abstract. Review the final template for any remaining inline if (this instanceof ...)-style differences and convert those into hooks as well.
Two traps to respect. First, do not pull up before the skeletons are identical — pulling up a "mostly same" method forces you to patch differences inside the parent with type checks, which is worse than the duplication you started with. Second, remember that subclasses now depend on the parent calling its steps in a promised order: this is a real contract. A teammate who later reorders two lines inside the template can silently break every subclass. Name the steps clearly, document the sequence, and keep a test that locks the order down.
College corner: that second trap is the fragile base class problem wearing the template's apron. A template method deliberately couples subclasses to the parent's internal calling sequence — that is the whole point — which means the sequence has graduated from "implementation detail" to "published contract." Treat the template's body the way you treat a public API: changes need the same care, the same review, and ideally a characterization test that fails if a step is dropped or reordered. The pattern is powerful precisely because it makes one kind of coupling official and names it.
A bigger real-life example 🧾
A school management system generates fee receipts in two formats: an HTML receipt for the parent portal and a plain-text receipt for SMS. The two generators grew separately, and by now each is forty lines of half-shared logic. Here is the trimmed-down truth of them:
// BEFORE: one algorithm, two bodies, already drifting
class HtmlReceipt {
generate(s: Student, items: FeeItem[]): string {
let out = "<div class='receipt'>";
out += `<h2>Sunrise Public School</h2>`;
out += `<p>${s.name} — Class ${s.className} — Roll ${s.roll}</p>`;
let total = 0;
for (const item of items) {
out += `<row>${item.label}: Rs.${item.amount}</row>`;
total += item.amount;
}
out += `<b>Total: Rs.${total}</b>`;
out += "</div>";
return out;
}
}
class SmsReceipt {
generate(s: Student, items: FeeItem[]): string {
let out = "SUNRISE PUBLIC SCHOOL\n";
out += `${s.name} | Cl ${s.className} | Roll ${s.roll}\n`;
let total = 0;
for (const item of items) {
out += `${item.label}: Rs.${item.amount}\n`;
total += item.amount;
}
out += `TOTAL: Rs.${total}`;
// bug fixed here last month... but not in HtmlReceipt:
return out.trim();
}
}The procedure is identical: school header, student line, loop over fee items accumulating a total, total line, finish. Only the formatting of each piece differs. Apply the two movements — extract same-named steps, then lift the skeleton:
// AFTER: the algorithm lives once; formats fill in the blanks
abstract class FeeReceipt {
generate(s: Student, items: FeeItem[]): string { // template method
let out = this.header();
out += this.studentLine(s);
let total = 0;
for (const item of items) {
out += this.feeLine(item);
total += item.amount;
}
out += this.totalLine(total);
return this.finish(out);
}
protected abstract header(): string;
protected abstract studentLine(s: Student): string;
protected abstract feeLine(item: FeeItem): string;
protected abstract totalLine(total: number): string;
protected finish(out: string): string { return out; } // hook
}
class HtmlReceipt extends FeeReceipt {
protected header() { return "<div class='receipt'><h2>Sunrise Public School</h2>"; }
protected studentLine(s: Student) {
return `<p>${s.name} — Class ${s.className} — Roll ${s.roll}</p>`;
}
protected feeLine(i: FeeItem) { return `<row>${i.label}: Rs.${i.amount}</row>`; }
protected totalLine(t: number) { return `<b>Total: Rs.${t}</b></div>`; }
}
class SmsReceipt extends FeeReceipt {
protected header() { return "SUNRISE PUBLIC SCHOOL\n"; }
protected studentLine(s: Student) {
return `${s.name} | Cl ${s.className} | Roll ${s.roll}\n`;
}
protected feeLine(i: FeeItem) { return `${i.label}: Rs.${i.amount}\n`; }
protected totalLine(t: number) { return `TOTAL: Rs.${t}`; }
protected finish(out: string) { return out.trim(); } // SMS-only need
}Notice three quiet victories. The total-accumulation logic — actual business logic — now exists once, so the "wrong total" class of bug can only happen in one place. The trim() fix that SMS got and HTML missed is now an honest, visible, named difference (finish) instead of an accidental one. And when the school adds a WhatsApp receipt next term, the new class is five small formatting methods — the algorithm comes free.
Measure that last claim. Count the lines a developer must write for each new receipt format, before and after:
The whole school project's experience, as the team lived it:
The same refactoring in Python 🐍
Python expresses the result especially cleanly with abc. Here is a data importer that reads files into a database — CSV and JSON versions had duplicated the whole pipeline; only the parsing step truly differed:
from abc import ABC, abstractmethod
class FileImporter(ABC):
def run(self, path: str) -> int: # the template method
raw = self._read(path) # shared
records = self._parse(raw) # the blank: varies per format
valid = [r for r in records if self._is_valid(r)] # shared
self._before_save(valid) # hook: default does nothing
return self._save(valid) # shared
def _read(self, path: str) -> str:
with open(path, encoding="utf-8") as f:
return f.read()
@abstractmethod
def _parse(self, raw: str) -> list[dict]: ...
def _is_valid(self, record: dict) -> bool:
return bool(record.get("id"))
def _before_save(self, records: list[dict]) -> None:
pass # hook — override if needed
def _save(self, records: list[dict]) -> int:
# one shared, well-tested save path for every format
return database.bulk_insert(records)
class CsvImporter(FileImporter):
def _parse(self, raw: str) -> list[dict]:
header, *rows = raw.splitlines()
keys = header.split(",")
return [dict(zip(keys, row.split(","))) for row in rows]
class JsonImporter(FileImporter):
def _parse(self, raw: str) -> list[dict]:
import json
return json.loads(raw)
def _before_save(self, records: list[dict]) -> None:
records.sort(key=lambda r: r["id"]) # JSON feeds arrive unsortedPython-specific notes:
@abstractmethodplus inheriting fromABCgives the same compiler-style safety as TypeScript'sabstract: instantiating a subclass that forgot_parseraises immediately, not at some later call.- The leading underscore signals "step method — called by the template, not by clients." Keep the template (
run) as the only public entry point so nobody invokes half an algorithm. - Hooks are idiomatic here:
_before_savedefaults topass, and only the subclass that cares overrides it. - Python's standard library itself is full of this exact shape — for example,
unittest.TestCase.run()is a template method callingsetUp, the test, andtearDownin a fixed order you do not control. You have been using this pattern all along.
IDE support 🛠️
There is no single "Form Template Method" button in mainstream IDEs — it is a composite refactoring — but every ingredient is automated, which keeps each step safe:
- IntelliJ IDEA / PyCharm / Rider: Extract Method (Ctrl+Alt+M) isolates the varying steps; Pull Members Up lifts the unified skeleton and can mark the remaining steps
abstractin the same dialog. Extract Superclass creates the parent when the duplicated classes have none. - ReSharper (Visual Studio, C#): Extract Method, Pull Members Up (with a "Make abstract" checkbox per member), and Extract Superclass cover the full sequence.
- VS Code (TypeScript): built-in Extract to method refactoring handles Step 2; the pull-up is manual, but TypeScript's
abstractkeyword and the compiler verify nothing was missed — any subclass missing a step refuses to compile.
The research literature even calls this one of the trickier refactorings to automate fully, because deciding what counts as the same step needs human judgment. The IDE moves the code; you supply the meaning.
Benefits and risks ⚖️
| Benefits | Risks / costs |
|---|---|
| Removes structural duplication that line-by-line diff tools cannot even see | If step orders are not truly identical, forcing one template distorts both procedures |
| The algorithm's order lives in exactly one place — fix it once, every variant benefits | Subclasses depend on the parent's calling order: editing the template can silently break overrides (fragile base class) |
| New variants are tiny classes — only the blanks, never the skeleton | Over-fragmenting into a dozen one-line hooks can hurt readability more than the duplication did |
| The compiler enforces that every variant answers every required step | Variation is welded to the hierarchy at compile time — steps cannot be swapped at runtime |
| Produces the well-known Template Method pattern, with named extension points | Each class supports only one template per method name; competing variations may demand Strategy instead |
Which smells does it cure? 👃
| Smell | How Form Template Method helps |
|---|---|
| Duplicate Code | The duplicated skeleton — invisible to diffs — collapses into one template method |
| Shotgun Surgery | A change to the procedure edits one template instead of every subclass copy |
| Divergent copies | Variants physically cannot drift in order, because only one copy of the order exists |
| Oversized subclasses | Each subclass shrinks to its genuine differences — a handful of small step methods |
| Lazy Class (prevention) | New variants are small but meaningful classes: every line they contain is a real difference |
Quick revision box 📦
+------------------------------------------------------------------+
| FORM TEMPLATE METHOD - REVISION CARD |
+------------------------------------------------------------------+
| Problem : sibling subclasses run the SAME STEPS in the SAME |
| ORDER, differing only in step details. |
| (Two biryani recipes, four identical steps.) |
| |
| Solution : 1. extract each differing part into a method with |
| the SAME NAME in every subclass |
| 2. when skeletons are textually identical, |
| PULL UP the method -> the TEMPLATE METHOD |
| 3. declare varying steps abstract on the parent |
| 4. optional steps become HOOKS (default body) |
| |
| Produces : the Template Method design pattern |
| (Hollywood principle: don't call us, we call you) |
| Rule : skeletons must MATCH EXACTLY before pulling up |
| Not for : differing step orders, or steps that must swap |
| at runtime (use Strategy / delegation instead) |
+------------------------------------------------------------------+Practice exercise ✏️
Your turn. An e-commerce backend ships orders through two couriers, and the two methods below have already drifted once (spot it):
class BlueDartShipper {
ship(order: Order): Label {
if (!order.address.pincode) throw new Error("Pincode required");
const weight = order.items.reduce((w, i) => w + i.weightKg, 0);
const cost = 50 + weight * 12; // BlueDart pricing
const tracking = `BD-${order.id}-${Date.now()}`; // BlueDart format
audit.log(`Shipped ${order.id} via BlueDart`);
return new Label(order.address, cost, tracking);
}
}
class DelhiveryShipper {
ship(order: Order): Label {
if (!order.address.pincode) throw new Error("Pincode required");
const weight = order.items.reduce((w, i) => w + i.weightKg, 0);
const cost = weight <= 1 ? 40 : 40 + (weight - 1) * 15; // Delhivery pricing
const tracking = `DLV${order.id}X${order.address.pincode}`; // Delhivery format
return new Label(order.address, cost, tracking);
// note: no audit.log here — drift or design?
}
}Work through it:
- Mark every line shared or varying. You should find exactly two genuinely varying steps (pricing and tracking format) and one suspicious difference (the missing audit log). Decide: is the missing log an accidental drift to repair, or a real difference to preserve as a hook?
- Extract
calculateCost(weight)andtrackingCode(order)in both classes with identical signatures. Compile and test after each extraction. - Diff the two
shipbodies. When they match character for character, create aShippersuperclass and pullshipup as the template method. - Declare the two varying steps
protected abstract. If you decided the audit log is a real difference, add anafterShip(order)hook with an empty default and override it inBlueDartShipper. - Prove the payoff: add an
EkartShipperwith flat Rs. 60 pricing and tracking formatEK/<id>. Count the lines you wrote — it should be under ten, just like Figure 8 promised — and confirm you edited zero existing files apart from wiring it up. - Sketch the Figure 6 sequence diagram for your
Shipper: which calls go down from the template, and which results come back up? If any arrow points from a subclass to the template's algorithm, you have broken the Hollywood principle — fix it. - Bonus thinking: the business now wants couriers chosen per order at checkout time, and a "rate comparison" screen that runs all pricing rules against one order. Does the template method still fit, or has the variation just demanded to become a swappable object? One sentence — and that sentence is the doorway to the next lesson, Replace Inheritance with Delegation.
If your step 1 answer was "the missing audit log is exactly how invisible structural duplication causes drift — fix it or name it, never leave it accidental," you have understood the deepest point of this whole lesson. Priya would print your recipe card without a single correction. Well done.
Frequently asked questions
- How is Form Template Method different from just using Pull Up Method?
- Pull Up Method works when two subclass methods are textually identical — you lift the one shared body into the parent. Form Template Method handles the harder case where the methods are only structurally the same: identical step order but different step details. You first reshape the methods until their skeletons match, then pull up the skeleton, leaving the differing steps behind as overridable operations.
- Is Form Template Method the same thing as the Template Method design pattern?
- They are two roads to the same building. The Template Method pattern is the destination — a superclass method that defines an algorithm's skeleton while subclasses fill in certain steps. Form Template Method is the refactoring road that gets you there from existing duplicated code, rather than designing the pattern up front.
- What is a hook method and when should I use one instead of an abstract step?
- An abstract step forces every subclass to provide an implementation. A hook is a step with a sensible default — often an empty body — that subclasses may override but are not forced to. Use abstract steps for parts every variant genuinely differs on, and hooks for optional extension points that only some variants care about.
- What if my two methods have similar steps but in a different order?
- Then stop — this refactoring does not apply. The template method's whole value is that the step order is genuinely invariant across all subclasses. Forcing two differently-ordered procedures into one skeleton distorts both. Either refactor them separately, or look for a smaller shared portion whose order really is fixed.
- When should I prefer the Strategy pattern over a template method?
- When the varying steps need to change at runtime, be combined freely, or be tested in isolation. A template method welds the variation to a class hierarchy at compile time. If you need to swap a step on a live object, hold the step as a separate object instead — that is Strategy, reached by Replace Inheritance with Delegation.
Further reading
Related Lessons
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.
Duplicate Code: Writing the Same Address on 50 Wedding Cards
Learn the Duplicate Code smell with a wedding card story. Understand DRY, the Rule of Three, and how Extract Method removes dangerous copy-paste code.
Pull Up Method: One Instruction Sheet for the Whole School
Learn the Pull Up Method refactoring with a school leave-application story — move methods duplicated across subclasses into the superclass, with safe steps in TypeScript and C#, IDE dialogs, and when to choose Form Template Method instead.
Extract Method: Turn One Giant Function into Small Named Helpers
Learn Extract Method step by step. Pull a messy block out of a long function, give it a clear name, and make your code read like a clean to-do list.