6.5. Template Method

  • EN: Template Method

  • PL: Metoda szablonowa

  • Type: class

The Template Method design pattern is a behavioral design pattern that defines the skeleton of an algorithm in a method, deferring some steps to subclasses. It lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.

Here's a simple example of the Template Method pattern in Python:

>>> class AbstractClass:
...     def template_method(self):
...         self.base_operation1()
...         self.required_operations1()
...         self.base_operation2()
...         self.hook1()
...
...     def base_operation1(self):
...         print("Base operation1")
...
...     def base_operation2(self):
...         print("Base operation2")
...
...     def required_operations1(self):
...         pass
...
...     def hook1(self):
...         pass
...
>>> class ConcreteClass1(AbstractClass):
...     def required_operations1(self):
...         print("ConcreteClass1 says: Implemented Operation1")
...
...     def hook1(self):
...         print("ConcreteClass1 says: Overridden Hook1")
...
>>> class ConcreteClass2(AbstractClass):
...     def required_operations1(self):
...         print("ConcreteClass2 says: Implemented Operation2")
...
>>> concrete_class = ConcreteClass1()
>>> concrete_class.template_method()
Base operation1
ConcreteClass1 says: Implemented Operation1
Base operation2
ConcreteClass1 says: Overridden Hook1

In this example, the AbstractClass provides a method called template_method() that defines a set of steps to execute an algorithm. Some of these steps (required_operations1()) are implemented directly in the AbstractClass, but one (hook1()) is left undefined. The ConcreteClass1 and ConcreteClass2 subclasses provide the implementation for the undefined hook1() method.

6.5.1. Pattern

  • Bank application with audit trail (all actions)

  • Record task history

  • Audits

../../_images/designpatterns-templatemethod-pattern-1.png
../../_images/designpatterns-templatemethod-pattern-2.png

6.5.2. Problem

  • Duplicated code

  • Not enforced to record in audit trail

design-patterns/behavioral/img/designpatterns-templatemethod-problem.png

from dataclasses import dataclass


class AuditTrail:
    def record(self) -> None:
        print('Audit')


@dataclass
class TransferMoneyTask:
    audit_trail: AuditTrail

    def execute(self):
        self.audit_trail.record()
        print('Transfer Money')


@dataclass
class GenerateReportTask:
    audit_trail: AuditTrail

    def execute(self):
        self.audit_trail.record()
        print('Generate Report')


if __name__ == '__main__':
    audit_trail = AuditTrail()
    task = TransferMoneyTask(audit_trail)
    task.execute()
    # Audit
    # Transfer Money

6.5.3. Solution

../../_images/designpatterns-templatemethod-solution.png
../../_images/designpatterns-templatemethod-vs-inheritance.png
../../_images/designpatterns-templatemethod-vs-strategy.png

from abc import ABC, abstractmethod
from dataclasses import dataclass


class AuditTrail:
    def record(self) -> None:
        print('Audit')


@dataclass
class Task(ABC):
    audit_trail: AuditTrail = AuditTrail()

    def execute(self) -> None:
        self.audit_trail.record()
        self.do_execute()
        print('Transfer Money')

    @abstractmethod
    def do_execute(self) -> None:
        pass

class GenerateReportTask(Task):
    def do_execute(self) -> None:
        print('Generate Report')

class TransferMoneyTask(Task):
    def do_execute(self) -> None:
        print('Transfer Money')


if __name__ == '__main__':
    task = TransferMoneyTask()
    task.execute()
    # Audit
    # Transfer Money

6.5.4. Assignments