This is part of my series on some common PHP design patterns. Today I'm looking at the Template Method Pattern.
I honestly had no idea that this design pattern was even called this until a while ago. It is a common way of doing things, and if you code in an object orientated programming language then you almost certainly just do this naturally.
What is The Template Method Pattern?
It is a commonly used design pattern (especially in PHP, I think) that makes use of abstract classes.
I think the best way to explain programming concepts is with some easy to understand code.
An example - 'before' converting some code to use the template method
Let's pretend you are a vehicle manufacturer, creating both cars and vehicles.
You have 4 basic steps in creating a vehicle:
addEngine()
(cars and motorbikes have different engines),addWheels()
(again, cars and motorbikes have different wheels),paintVehicle()
(but the basic process of painting is the same for cars and motorbikes (so the code is identical))sellVehicle()
(and again, the basic process of selling a car or motorbike is the same, so the code is identical
This is a very oversimplified example to get the point across. The code below also will be simplified to make explaining this easier)
It should be quite obvious how to handle this very simple example. A lot of the code can be shared. But let's begin with a bad implementation, spread over three files:
CarMaker.php
This is the class that contains all of the code for each step of making a car
<?php
class CarMaker
{
public function addWheels()
{
echo __METHOD__ . " 4 car Wheels were added\n";
}
public function addEngine()
{
echo __METHOD__ . " Car Engine was added\n";
}
public function paintVehicle()
{
echo __METHOD__ . " Painting vehicle";
}
public function sellVehicle()
{
echo __METHOD__ . " selling Vehicle";
}
}
MotorbikeMaker.php
This is very similar to the above class. A lot of code was copy/pasted. However please note the addWheels()
and addEngine()
are different.
<?php
class MotorbikeMaker
{
public function addWheels()
{
// different than the CarMaker::addWheels() method
echo __METHOD__ . " 2 motorbike Wheels were added\n";
}
public function addEngine()
{
// this is different than the addEngine() above, as motorbike and car engines are different
echo __METHOD__ . " 1 Motorbike Engine was added\n";
}
public function paintVehicle()
{
// same as previous class' implementation of paintVehicle()
echo __METHOD__ . " Painting vehicle";
}
public function sellVehicle()
{ // this is the same as the previous class' implementation of sellVehicle()
echo __METHOD__ . " selling Vehicle";
}
}
MakeVehicles.php
This is the file that calls the above methods.
<?php
$carmaker = new CarMaker;
$carmaker->addEngine();
$carmaker->addWheels();
$carmaker->paintVehicle();
$carmaker->sellVehicle();
echo "\n-----------------------------\n\n";
$motorbikemaker = new MotorbikeMaker;
$motorbikemaker->addEngine();
$motorbikemaker->addWheels();
$motorbikemaker->paintVehicle();
$motorbikemaker->sellVehicle();
Hopefully you can see some obvious ways how this can be improved.
Now time for the 'after' - with the template method. First, let's create a main abstract class
We can create one main abstract class, which calls the 4 template steps in one method, and has the code for any shared methods.
i.e. to put another way: for paintVehicle() and sellVehicle(), we will have a fully implemented method. For addEngine() and addWheels() (which have different implementations for each vehicle type) we will just have an abstract method, which will have to be implemented in a subclass)
VehicleMaker.php
<?php
abstract class VehicleMaker
{
// these two MUST be implemented by a sub class
// as the wheel or engines are specific to the type of vehicle
abstract protected function addWheels();
abstract protected function addEngine();
// however the process of painting (or selling) doesn't change, so we can have the same methods for all vehicle types
protected function paintVehicle()
{
echo __METHOD__ . " Painting vehicle\n";
}
// selling a vehicle NEVER changes, so this should be marked as a final function so subclasses can implement their own version of it
final protected function sellVehicle()
{
echo __METHOD__ . " selling Vehicle\n";
}
// we ALWAYS only have the same steps, in the same order, so we can use this method as the template
// like sellVehicle(), this should never be implemented
final public function template()
{
echo __METHOD__ . " making a new vehicle from scratch\n";
$this->addEngine();
$this->addWheels();
$this->paintVehicle();
$this->sellVehicle();
echo __METHOD__ . " vehicle was made and sold! \n\n---------------\n";
}
}
That abstract class covers paintVehicle()
and sellVehicle()
, however addWheels()
and addEngine()
are only abstract methods so they need to implemented in sub classes.
Please note the use of final
, which is important for the template pattern. We don't ever want a subclass implementing their own way of selling a vehicle (but maybe they can do it for painting, there could be some specialised vehicle that needs its own painting procedure)
CarMaker_2.php
(Ignore the bad naming of this class. It is just to make it easier to work out what class I'm talking about)
This subclass must extend the main abstract template class (VehicleMaker). It only needs to implement two methods.
<?php
class CarMaker_2 extends VehicleMaker
{
protected function addWheels()
{
echo __METHOD__ . " added 4 car wheels\n";
}
protected function addEngine()
{
echo __METHOD__ . " added a car engine\n";
}
}
MotorbikeMaker_2.php
Again please ignore the bad naming of files and classes here. But this class is very similar to CarMaker_2, just with a motorbike engine and wheels instead of car ones.
<?php
class MotorbikeMaker_2 extends VehicleMaker
{
protected function addWheels()
{
echo __METHOD__ . " added 2 motorbike wheels\n";
}
protected function addEngine()
{
echo __METHOD__ . " added a motorbike engine\n";
}
}
Putting it all together
And these methods are called with something like this:
<?php
$motorbikemaker_2 = new MotorbikeMaker_2;
$motorbikemaker_2->template();
$carmaker_2 = new CarMaker_2;
$carmaker_2->template();
When to use the template method in the real world
- When you end up repeating a lot of the same code, and the code can be split up into similar 'steps' or 'sections'.
- When you want to be able to extend code easily ("open for extension"), but control the core parts of it ("closed for modification") - in the example above we could easily extend it by adding a new vehicle type, but the selling or actual template making methods should not be changed.
- When you have a set of steps (algorithm), that might have multiple implementations. The base set of steps (algorithm) will be defined in the base class, but sub classes can create their own implementation of each step that replace the base class steps.
The template method in the real world (examples of the template method)
The above example was a little silly and very basic. However, this method is used all the time in PHP development (and other languages).
They can be used for things such as:
- Payments - you might have a base class that saves payment info, redirects the user to the payment gateway. However you might have subclasses that have methods for telling the base class which URL to redirect to, and a method for receiving the information from the payment provider (and processing it before sending it to the base methods of saving the new subscription info). So you might have subclasses for Paypal, another one for Stripe subscriptions, etc.
- Creating invoices - a base class will handle things such as generating and/or getting the invoice. But the subclasses might have different implementations for rendering the invoice. You might have a PDF output class, or a Word Document output class. They will use the base class, but have their own implementation of the actual output.
I think this is one design pattern that almost everyone uses but doesn't know the name of it. As soon as you learn about inheritance you will probably end up doing something like this (maybe without abstract classes, but just normal classes).
Comments →PHP Template Method Pattern