Design Patterns: Template Method
• • 6 min readThere are 23 classic design patterns, which are described in the original book, Design Patterns: Elements of Reusable Object-Oriented Software
. These patterns provide solutions to particular problems, often repeated in the software development.
In this article, I'm going to describe the how the Template Pattern; and how and when it should be applied.
Template Method Pattern: Basic Idea
The template method pattern is a behavioral design pattern that defines the program skeleton of an algorithm in an operation, deferring some steps to subclasses — Wikipedia
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure. - Design Patterns: Elements of Reusable Object-Oriented Software
The main feature of this pattern is an algorithm which changes slightly between different kinds of classes. These parts in common are repeated in the different algorithms when implemented in a concrete class.
The following code shows the classic problem where you must repeat parts of an algorithm (copy/paste), with very small changes:
class A {
myMethod() {
step1(); // Common part
console.log('specific implementation to Class A');
step3(); //Common part
}
}
class B {
myMethod() {
step1(); // Common part
console.log('specific implementation to Class B');
step3(); //Common part
}
}
We can make this code a lot cleaner by using the Template-method Pattern, which allows us to avoid repeating code in the different implementations of the algorithm. The UML's diagram of this pattern is the following:
Note the abstract class, containing the template method and the private methods in common. The template-method describes the algorithm in different steps. The steps in common are implemented in the abstract class, while that the concrete steps, different in each concrete class, are implemented in said concrete class.
Template-Method Pattern: When To Use
- The problem resolved by the Template-Method Pattern, is the use of an algorithm which has different variations. You would need to split your algorithm in different steps, implemented in the abstract class when in common between the different implementations. In other hand, the steps which are different will be implemented in the concrete classes.
- Another interesting case where you would detect the need of this pattern, is when you have copy/paste code (private functions) between different classes.
- Finally you may to use this pattern when most of your classes have related behaviours.
Template-Method Pattern: Advantages
The Template-Method Pattern has several advantages, summarised in the following points:
- It's fairly easy to create concrete implementations of an algorithm because you're removing common parts of the problem domain by the use of an abstract class.
- Clean code because you avoid duplicate code.
- Ever cleaner code because you separate the algorithm into private methods/functions, which are simpler and easier to test.
Template pattern: A Pokemon example using JavaScript
I will now show you how you can implement this pattern using JavaScript. Please bear in mind that Javascript lacks both interfaces and abstract classes. Therefore, the best way to understand this pattern is by using an example. In our case, I've thought of a problem in which there is an abstract class named Pokemon
that defines a Pokemon. A pokemon has a set of attributes such as name
, power
, attack
and defense
besides the classic toString
method. There is a classification of types of pokemon such as FightingPokemon
, PoisonPokemon
and GroundPokemon
which define a concrete method called calculateDamage
that calculates a numberical value depending on the attributes and the type of pokemon. The following UML diagram shows the scenario that I have just described.
The code which implements the actual situation using JavaScript is the following:
class Pokemon {
constructor(_pokemon) {
this.name = _pokemon.name || 'unknown';
this.power = _pokemon.power || 1;
this.attack = _pokemon.attack || 1;
this.defense = _pokemon.defense || 1;
}
toString() {
return `${this.name} - power: ${this.power}; attack: ${
this.attack
}; defense: ${this.defense}`;
}
}
class FightingPokemon extends Pokemon {
constructor(_pokemon) {
super(_pokemon);
}
calculateDamage() {
const multipliers = (1 / 2) * this.power * Math.random();
const impact = Math.floor((this.attack / this.defense) * multipliers) + 1;
console.log('Pokemon damage is:', impact);
}
}
class PoisonPokemon extends Pokemon {
constructor(_pokemon) {
super(_pokemon);
}
calculateDamage() {
const multipliers = (1 / 2) * this.power * Math.random();
const impact = Math.floor((this.attack - this.defense) * multipliers) + 1;
console.log('Pokemon damage is:', impact);
}
}
class GroundPokemon extends Pokemon {
constructor(_pokemon) {
super(_pokemon);
}
calculateDamage() {
const multipliers = (1 / 2) * this.power * Math.random();
const impact = Math.floor((this.attack + this.defense) * multipliers) + 1;
console.log('Pokemon damage is:', impact);
}
}
The method calculateDamage
is repeated in each concrete class, i.e., a smell code there is (copy/paste - duplication code).This method can be split into different steps, like so:
calculateDamage(){
const multipliers = this.calculateMultipliers();
const impact = this.calculateImpact(multipliers);
const showDamage(impact);
}
Note that our method has been divided into three functions. In fact, two are common and one is specific, depending on the class that implements it (calculateImpact
). Our Template-Method
Pattern has been successfuly applied. In the following UML you can see the version update using the Template-Method.
The class Pokemon
is the following:
class Pokemon {
constructor(_pokemon) {
this.name = _pokemon.name || 'unknown';
this.power = _pokemon.power || 1;
this.attack = _pokemon.attack || 1;
this.defense = _pokemon.defense || 1;
}
toString() {
return `${this.name} - power: ${this.power}; attack: ${
this.attack
}; defense: ${this.defense}`;
}
calculateMultiplier() {
//Step 1 - Common
return (1 / 2) * this.power * Math.random();
}
showDamage(damage) {
// Step 3 - Common
console.log('Pokemon damage is:', damage);
}
calculateDamage() {
const multipliers = this.calculateMultiplier(); //Step 1;
const damage = this.calculateImpact(multipliers); //Step 2;
this.showDamage(damage); //Step 3;
}
}
You may have noted that the method this.calculateImpact
method is not implemented in this class. This is because the concrete implementation will be in the concrete class. This Pokemon class is the abstract class in our problem.
The next step consists of the implementation of the concrete classes which have the calculateImpact
method.
class FightingPokemon extends Pokemon {
constructor(_pokemon) {
super(_pokemon);
}
calculateImpact(multipliers) {
return Math.floor((this.attack / this.defense) * multipliers) + 1;
}
}
class PoisonPokemon extends Pokemon {
constructor(_pokemon) {
super(_pokemon);
}
calculateImpact(multipliers) {
return Math.floor((this.attack - this.defense) * multipliers) + 1;
}
}
class GroundPokemon extends Pokemon {
constructor(_pokemon) {
super(_pokemon);
}
calculateImpact(multipliers) {
return Math.floor((this.attack + this.defense) * multipliers) + 1;
}
}
Finally, the client/context code where we use the concrete Pokemon is the following:
// Client-Context
const passimian = new FightingPokemon({
name: 'Passimian',
attack: 10,
power: 10,
defense: 10
});
console.log(passimian.toString());
passimian.calculateDamage();
const poipole = new PoisonPokemon({
name: 'Poipole',
attack: 10,
power: 10,
defense: 10
});
console.log(poipole.toString());
poipole.calculateDamage();
const mudsdale = new GroundPokemon({
name: 'Mudsdale',
attack: 10,
power: 10,
defense: 10
});
console.log(mudsdale.toString());
mudsdale.calculateDamage();
This code creates concrete Pokemon, which invoke its calculateDamage
. Its implementation is transparent to the client/context, however, the code is not repeated. Finally, I've created two npm scripts that run the code before and after applying the Template-Method pattern.
npm run step0
npm run step1
Conclusion
Template-Method Pattern is a pattern which can avoids duplicating code in your project, when an algorithm has both invariant and variant part, the latter depending on the concrete class. In this post you have been able to observe a simple implementation using JavaScript language, which lacks interfaces/abstract. In the case that you use a programming language which does have interface/abstract, you may follow the UML pattern.
The most important thing is not implement the pattern as I've shown you, but to be able to recognise the problem which this specific pattern can resolve, and when you may or may not implement said pattern. This is crucial, since implementation will vary depending on the programming language you use.
More more more...
- Design Patterns: Elements of Reusable Object-Oriented Software by Gamma, Helm, Johnson, & Vlissides, Addison Wesley, 1995.
- The Template-Method Pattern — Wikipedia.
- https://www.dofactory.com/javascript/template-method-design-pattern
- https://github.com/sohamkamani/javascript-design-patterns-for-humans#-template-method
- https://blog.bitsrc.io/design-patterns-series-the-template-method-f1e49315ff6d
- The GitHub branch of this post is https://github.com/Caballerog/blog/tree/master/template-method-pattern