Card 03/ 09

CodeDifficulty: Intermediate3 min

Template Method Template in Python, Java and TypeScript

A final template method in Java, and the same intent shown by convention in Python and TypeScript.

The Template Method template is the pattern in its pure form, with the participant names from the class diagram. The template method prints a start and an end around two abstract steps and a hook, and two subclasses run it: one leaves the hook alone and one overrides it. The steps build the base class in Python, and the tabs give the complete program in three languages, each with its output.

Building the template one part at a time

Template method

Fixes the order, and is marked as not to be overridden.

py
@final
def template_method(self) -> None:
    print("base: start")
    self.step_one()
    self.hook()
    self.step_two()
    print("base: end")

Abstract steps

Must be written by every subclass.

py
@abstractmethod
def step_one(self) -> None: ...

Hook

Has a default that does nothing, so a subclass may override it or not.

py
def hook(self) -> None:
    pass

Concrete class

Supplies the steps, and here also the hook.

py
class ConcreteClassB(AbstractClass):
    def hook(self) -> None:
        print("B: hook")

The complete template

py
from abc import ABC, abstractmethod
from typing import final


class AbstractClass(ABC):
    @final
    def template_method(self) -> None:
        print("base: start")
        self.step_one()
        self.hook()
        self.step_two()
        print("base: end")

    @abstractmethod
    def step_one(self) -> None: ...

    @abstractmethod
    def step_two(self) -> None: ...

    def hook(self) -> None:
        pass


class ConcreteClassA(AbstractClass):
    def step_one(self) -> None:
        print("A: step one")

    def step_two(self) -> None:
        print("A: step two")


class ConcreteClassB(AbstractClass):
    def step_one(self) -> None:
        print("B: step one")

    def step_two(self) -> None:
        print("B: step two")

    def hook(self) -> None:
        print("B: hook")


for instance in (ConcreteClassA(), ConcreteClassB()):
    instance.template_method()
OutputRan with python:3.12-slim (Python 3.12.13)
base: start
A: step one
A: step two
base: end
base: start
B: step one
B: hook
B: step two
base: end
`@final` from `typing` is read by type checkers, and ignored when the program runs.
java
public class TmTemplate {
    public static void main(String[] args) {
        for (AbstractClass instance : new AbstractClass[] {new ConcreteClassA(), new ConcreteClassB()}) {
            instance.templateMethod();
        }
    }
}

abstract class AbstractClass {
    public final void templateMethod() {
        System.out.println("base: start");
        stepOne();
        hook();
        stepTwo();
        System.out.println("base: end");
    }

    protected abstract void stepOne();

    protected abstract void stepTwo();

    protected void hook() {}
}

class ConcreteClassA extends AbstractClass {
    protected void stepOne() {
        System.out.println("A: step one");
    }

    protected void stepTwo() {
        System.out.println("A: step two");
    }
}

class ConcreteClassB extends AbstractClass {
    protected void stepOne() {
        System.out.println("B: step one");
    }

    protected void stepTwo() {
        System.out.println("B: step two");
    }

    @Override
    protected void hook() {
        System.out.println("B: hook");
    }
}
OutputRan with eclipse-temurin:21-jdk (Java 21.0.12)
base: start
A: step one
A: step two
base: end
base: start
B: step one
B: hook
B: step two
base: end
`final` makes the compiler refuse any subclass that overrides `templateMethod`.
ts
abstract class AbstractClass {
  templateMethod(): void {
    console.log("base: start");
    this.stepOne();
    this.hook();
    this.stepTwo();
    console.log("base: end");
  }

  protected abstract stepOne(): void;

  protected abstract stepTwo(): void;

  protected hook(): void {}
}

class ConcreteClassA extends AbstractClass {
  protected stepOne(): void {
    console.log("A: step one");
  }

  protected stepTwo(): void {
    console.log("A: step two");
  }
}

class ConcreteClassB extends AbstractClass {
  protected stepOne(): void {
    console.log("B: step one");
  }

  protected stepTwo(): void {
    console.log("B: step two");
  }

  protected override hook(): void {
    console.log("B: hook");
  }
}

for (const instance of [new ConcreteClassA(), new ConcreteClassB()]) {
  instance.templateMethod();
}
OutputRan with node:22-slim (Node 22.23.1), TypeScript 5.9.3
base: start
A: step one
A: step two
base: end
base: start
B: step one
B: hook
B: step two
base: end
TypeScript has no `final`; the template method is protected only by convention.

Both subclasses run the same order, start, step one, hook, step two, end, and only B's output has a hook line. Neither subclass contains the sequence; it lives in AbstractClass alone.

The three languages differ in how they protect that sequence. Java's final is enforced: a subclass that overrides templateMethod does not compile. Python's typing.final is checked only by a type checker such as mypy, and TypeScript has no such keyword at all, so in both a subclass can replace the template method and the program will still run.