Usage examples: The Template Method pattern is quite common in Python frameworks. Developers often use it to provide framework users with a simple means of extending standard functionality using inheritance.
Identification: Template Method can be recognized if you see a method in base class that calls a bunch of other methods that are either abstract or empty.
Conceptual Example
This example illustrates the structure of the Template Method design pattern. It focuses on answering these questions:
What classes does it consist of?
What roles do these classes play?
In what way the elements of the pattern are related?
main.py: Conceptual example
from abc import ABC, abstractmethodclassAbstractClass(ABC):""" The Abstract Class defines a template method that contains a skeleton of some algorithm, composed of calls to (usually) abstract primitive operations. Concrete subclasses should implement these operations, but leave the template method itself intact. """deftemplate_method(self) ->None:""" The template method defines the skeleton of an algorithm. """ self.base_operation1() self.required_operations1() self.base_operation2() self.hook1() self.required_operations2() self.base_operation3() self.hook2()# These operations already have implementations.defbase_operation1(self) ->None:print("AbstractClass says: I am doing the bulk of the work")defbase_operation2(self) ->None:print("AbstractClass says: But I let subclasses override some operations")defbase_operation3(self) ->None:print("AbstractClass says: But I am doing the bulk of the work anyway")# These operations have to be implemented in subclasses.@abstractmethoddefrequired_operations1(self) ->None:pass@abstractmethoddefrequired_operations2(self) ->None:pass# These are "hooks." Subclasses may override them, but it's not mandatory# since the hooks already have default (but empty) implementation. Hooks# provide additional extension points in some crucial places of the# algorithm.defhook1(self) ->None:passdefhook2(self) ->None:passclassConcreteClass1(AbstractClass):""" Concrete classes have to implement all abstract operations of the base class. They can also override some operations with a default implementation. """defrequired_operations1(self) ->None:print("ConcreteClass1 says: Implemented Operation1")defrequired_operations2(self) ->None:print("ConcreteClass1 says: Implemented Operation2")classConcreteClass2(AbstractClass):""" Usually, concrete classes override only a fraction of base class' operations. """defrequired_operations1(self) ->None:print("ConcreteClass2 says: Implemented Operation1")defrequired_operations2(self) ->None:print("ConcreteClass2 says: Implemented Operation2")defhook1(self) ->None:print("ConcreteClass2 says: Overridden Hook1")defclient_code(abstract_class: AbstractClass) ->None:""" The client code calls the template method to execute the algorithm. Client code does not have to know the concrete class of an object it works with, as long as it works with objects through the interface of their base class. """# ... abstract_class.template_method()# ...if__name__=="__main__":print("Same client code can work with different subclasses:")client_code(ConcreteClass1())print("")print("Same client code can work with different subclasses:")client_code(ConcreteClass2())
Output.txt: Execution result
Same client code can work with different subclasses:
AbstractClass says: I am doing the bulk of the work
ConcreteClass1 says: Implemented Operation1
AbstractClass says: But I let subclasses override some operations
ConcreteClass1 says: Implemented Operation2
AbstractClass says: But I am doing the bulk of the work anyway
Same client code can work with different subclasses:
AbstractClass says: I am doing the bulk of the work
ConcreteClass2 says: Implemented Operation1
AbstractClass says: But I let subclasses override some operations
ConcreteClass2 says: Overridden Hook1
ConcreteClass2 says: Implemented Operation2
AbstractClass says: But I am doing the bulk of the work anyway