Introduction
The template method pattern is a behavourial design pattern which allows you to define the template, or the skeleton of an operation in a base class, while allowing the subclasses to override specific steps or all steps of the algorithm without changing its structure.
This pattern is very useful when you have a series of steps that needs to be performed in a specific order, but you need different implementations in different situations.
It looks like this:
The parts of this pattern are:
- The Producer is tasked with producing some product, which in this case can be a Car or a Bike. In order to produce these products, two steps are needed.
- BikeProducer and CarProducer are the two concrete Producers.
- The Client class uses both the step1 and the step2 method to obtain a product.
Implementation in Python
We will start by defining the VehicleTemplate base-class:
class VehicleTemplate:
def AddFrame(self) -> str:
pass
def AddWheels(self, number_of_wheels: int) -> str:
pass
This class has two methods:
- An AddFrame() method to create a frame, in our case represented by a string
- The AddWheels() method which creates the wheels for our vehicle, also represented by a string
Note that we make the types explicit. This helps in understanding and maintaining the code.
Now we can implement our first producer, CarProducer, which is a subclass of VehicleTemplate:
class CarProducer(VehicleTemplate):
def AddFrame(self) -> str:
return "Add frame for a car"
def AddWheels(self, number_of_wheels: int) -> str:
return f"Add {number_of_wheels} wheels for a car"
This is a very simple implementation of the VehicleTemplate class. Note the use of types, and the easy way string formatting is done in Python
The BikeProducer interface is very similar:
class BikeProducer(VehicleTemplate):
def AddFrame(self) -> str:
return "Add frame for a bike"
def AddWheels(self, number_of_wheels: int) -> str:
return f"Add {number_of_wheels} wheels for a bike"
All we need now is a client to put together our vehicles. This is called the Template Method:
def ProduceVehicle(template: VehicleTemplate, number_of_wheels: int) -> str:
frame = template.AddFrame()
wheels = template.AddWheels(number_of_wheels)
return f"{frame}, then {wheels}"
Line by line:
- We create a frame, using the AddFrame() method in the VehicleTemplate class
- Next we create the wheels using the AddWheels() method in the VehicleTemplate class, passing the number of wheels
- Next we return a formatted string with our product.
Time to test
Now we can test our template:
if __name__ == "__main__":
car_producer = CarProducer()
bike_producer = BikeProducer()
print(ProduceVehicle(car_producer, 4))
print(ProduceVehicle(bike_producer, 2))
Line by line:
- We instantiate a CarProducer object. Note that in Python, there is always a default parameterless constructor.
- Next we instantiate a BikeProducer object
- We pass both instances to the ProduceVehicle() function, to produce the desired vehicles.
Conclusion
The implementation of this pattern in Python quite straightforward and easy. Using subclassing makes this implementation extensible and elegant. Also adding type-annotations makes the code much clearer and maintable.