202
GOF behavioral

Template Method

Reference Wikipedia ↗
Template Method — sequence diagram
Plate 202 sequence diagram

The Template Method pattern defines the skeleton of an algorithm in a method, deferring some steps to subclasses. It allows one of the algorithm’s steps to be overridden by a subclass without changing the algorithm’s structure. This promotes code reuse and reduces redundancy by centralizing common logic while providing flexibility for specific variations.

This pattern is particularly useful when you have a process with several steps that are largely the same across different scenarios, but certain steps need to be customized. It’s also valuable when you want to enforce a specific order of operations, ensuring consistency while enabling extension. The abstract class implements the overall algorithm, while concrete classes provide implementations for the abstract steps.

Usage

The Template Method pattern is frequently used in scenarios like:

  • Framework Development: Creating the basic structure of a framework where the core logic is defined, and clients fill in the specific details.
  • Report Generation: Generating reports with a common format, but different sections depending on the type of report.
  • Data Processing Pipelines: Implementing a pipeline with stages that are consistent but have varying data transformation logic.
  • Game Development: Defining the basic flow of a game level, but allowing different levels to have their specific events or behaviors.

Examples

  1. Java I/O Streams: The InputStream class in Java employs the Template Method pattern. The read() method is the template method, defining the overall process of reading data. Subclasses like FileInputStream and ByteArrayInputStream override the read() method to provide the specific implementation for reading from a file or a byte array, respectively. The core logic of handling buffering and error checking remains in the InputStream class.

  2. Django’s Class-Based Views (CBVs): Django’s CBVs use this pattern extensively. A base class like View defines the as_get_view(), as_post_view() methods (the template method) outlining the request handling process. Then, different types of views (e.g., DetailView, ListView) inherit from View and override specific methods like get() or post() to perform tailored actions. The overall request-response cycle is managed in the base View class, ensuring consistency across all views.

  3. Scikit-learn Estimators: In the Python machine learning library scikit-learn, estimators (like LogisticRegression or DecisionTreeClassifier) often follow the Template Method pattern. The fit() method is a template method that defines the learning process. Subclasses implement more specific fitting algorithms while relying on the common infrastructure and validation mechanisms defined in the base estimator class.

Specimens

15 implementations
Specimen 202.01 Dart View specimen ↗

The Template Method pattern defines the skeleton of an algorithm in a base class, deferring some steps to subclasses. It allows subclasses to redefine certain steps of an algorithm without changing the algorithm’s structure. This promotes code reuse and avoids duplication.

The Dart example defines an abstract Game class with a play() method representing the algorithm’s skeleton. initialize(), makeMove(), and endGame() are abstract methods that subclasses must implement, providing the specific steps. Dart’s support for abstract classes and methods makes it a natural fit for this pattern. The concrete Cricket and Football classes inherit from Game and provide their own implementations for the abstract methods, customizing the game logic while maintaining the overall play() sequence.

abstract class Game {
  void initialize();
  void makeMove();
  void endGame();

  void play() {
    initialize();
    makeMove();
    endGame();
  }
}

class Cricket extends Game {
  @override
  void initialize() {
    print('Cricket Game Initializing: Setting up players and pitch.');
  }

  @override
  void makeMove() {
    print('Cricket Game: Batsman bats, Bowler bowls.');
  }

  @override
  void endGame() {
    print('Cricket Game Finished: Declaring the winner.');
  }
}

class Football extends Game {
  @override
  void initialize() {
    print('Football Game Initializing: Setting up teams and field.');
  }

  @override
  void makeMove() {
    print('Football Game: Players dribble and pass the ball.');
  }

  @override
  void endGame() {
    print('Football Game Finished: Declaring the winning team.');
  }
}

void main() {
  var cricketGame = Cricket();
  cricketGame.play();

  var footballGame = Football();
  footballGame.play();
}