Card 03/ 09
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.
@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.
@abstractmethod
def step_one(self) -> None: ...Hook
Has a default that does nothing, so a subclass may override it or not.
def hook(self) -> None:
passConcrete class
Supplies the steps, and here also the hook.
class ConcreteClassB(AbstractClass):
def hook(self) -> None:
print("B: hook")The complete template
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()base: start
A: step one
A: step two
base: end
base: start
B: step one
B: hook
B: step two
base: endpublic 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");
}
}base: start
A: step one
A: step two
base: end
base: start
B: step one
B: hook
B: step two
base: endabstract 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();
}base: start
A: step one
A: step two
base: end
base: start
B: step one
B: hook
B: step two
base: endBoth 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.
← → move through sections and cards