Design Patterns: Template Method

JavaScript NodeJS TypeScript

There 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:

Template-Method Pattern

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

  1. 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.
  2. Another interesting case where you would detect the need of this pattern, is when you have  copy/paste code (private functions) between different classes.
  3. 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.

Original problem using hierarchy.
Poipole Pokemon

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.

Template-Method Pattern

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.

Passimian Pokemon
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.

Mudsdale Pokemon

More more more...