The Template Method is a behavioural design pattern that defines the skeleton of an algorithm in a method, deferring some steps to subclasses. This pattern allows subclasses to redefine certain steps of an algorithm without changing the algorithm's structure. It promotes code reuse and ensures that the core algorithm remains consistent while allowing flexibility in the specific steps of the process.

The key components of this pattern are:

Example Scenario 1:

Implementation

// Abstract class
class DataProcessor {
  process() {
    this.readData();
    this.processData();
    this.writeData();
  }
  readData() {
    throw new Error("readData() must be implemented");
  }
  processData() {
    throw new Error("processData() must be implemented");
  }
  writeData() {
    throw new Error("writeData() must be implemented");
  }
}

// Concrete class
class CsvDataProcessor extends DataProcessor {
  readData() {
    console.log("Reading data from CSV file");
    this.data = "name,age\nJohn,30\nJane,25";
  }
  processData() {
    console.log("Processing CSV data");
    this.data = this.data.split("\n").map((row) => row.split(","));
  }
  writeData() {
    console.log("Writing data to CSV file");
    console.log(this.data);
  }
}

Test case

// Client
const csvProcessor = new CsvDataProcessor();
csvProcessor.process();

Explanation of the Implementation

In this implementation, the DataProcessor abstract class defines the process method, which acts as the template method outlining the steps of the algorithm: reading data, processing data, and writing data. The CsvDataProcessor concrete class implements these abstract methods to provide specific behaviour for handling CSV data. When the process method is called on the csvProcessor instance, it follows the defined steps, ensuring consistency while allowing the specific steps to be customised.

Example Scenario 2:

Implementation

// Abstract class
class Game {
  play() {
    this.initialize();
    this.startPlay();
    this.endPlay();
  }
  initialize() {
    throw new Error("initialize() must be implemented");
  }
  startPlay() {
    throw new Error("startPlay() must be implemented");
  }
  endPlay() {
    throw new Error("endPlay() must be implemented");
  }
}

// Concrete class for Soccer
class Soccer extends Game {
  initialize() {
    console.log("Soccer Game Initialized. Start playing.");
  }
  startPlay() {
    console.log("Soccer Game Started. Enjoy the game!");
  }
  endPlay() {
    console.log("Soccer Game Finished!");
  }
}

// Concrete class for Basketball
class Basketball extends Game {
  initialize() {
    console.log("Basketball Game Initialized. Start playing.");
  }
  startPlay() {
    console.log("Basketball Game Started. Enjoy the game!");
  }
  endPlay() {
    console.log("Basketball Game Finished!");
  }
}

Test case

// Client
const soccer = new Soccer();
soccer.play();

const basketball = new Basketball();
basketball.play();

Explanation of the Implementation

In this implementation, the Game abstract class defines the template method play, which includes the steps to initialise, start, and end a game. The Soccer and Basketball concrete classes implement these steps, providing specific behaviour for each type of game. When the play method is called on instances of Soccer and Basketball, it follows the defined algorithm, ensuring that the games are initialised, started, and ended in a consistent manner while allowing for specific implementations of each step for different games. This demonstrates how the Template Method pattern can be used to structure the workflow of different games while maintaining a consistent overall process.