Stencil method

from Wikipedia, the free encyclopedia
Class diagram of a template method implementation

The template method ( English template pattern method ) is employed in the software development design pattern , with the partial steps of the algorithm can be variable. It belongs to the category of behavioral patterns (Engl. Behavioral patterns ). The pattern is one of the so-called four-band design patterns (GoF).

functionality

In the template method design pattern, the skeleton of an algorithm is defined in an abstract class. The specific design of the individual steps is delegated to subclasses . This makes it possible to change or overwrite individual steps of the algorithm without having to modify the underlying structure of the algorithm. The template method calls abstract methods that are only defined in the subclasses. These methods are also known as slide-in methods.

In addition, hook operations can be called at certain points in the template method, the standard implementation of which does nothing in the abstract class. In this way, you can insert additional functionality at predefined points in the algorithm.

As a variant, the insertion methods or hook operations can also have a standard implementation that can be used by the specific classes, but does not have to.

An example of this is found in the I / O - data stream - Programming of Java. There one implements a OutputStreamconcrete method for writing a byte array. This method uses a single byte write method to gradually write the entire array. The method for the single byte is still abstract, however, since one OutputStreamitself is not yet specific. Classes like FileOutputStreamcan implement this method. You then inherit an already implemented method of writing a byte array.

Policy-based design

Policy-Based Design is a more general design pattern in which not only algorithms but entire classes are built up like templates. Methods or algorithms as well as stored data sets, base classes and interfaces can then be exchanged within the skeleton structure. As a rule , this requires template meta programming , as is available in C ++ and D , but can theoretically also be implemented using constructs in some scripting languages ​​( eval , macros , autoloading of source code, etc.), but hardly in languages ​​such as C # or Java .

example

The rough sequence of a board game like chess or Monopoly always looks the same: A greeting is pronounced and the board is set up. Then it is the turn of the players in turn until the game is over. Finally, the winner will be determined.

The skeleton of this algorithm, which is always the same, can be implemented in its basic features in a template method of an abstract class Game. In order to implement a specific game, the abstract methods of the template method (which differ for every board game) must be implemented in a specific child class. Here is an example in C ++ .

#include <iostream>
#include <cstring>

using std::cout;
using std::endl;

class Game
{
protected:
  int m_iPlayersCount;

  Game(const int iPLAYERS_COUNT)
  {
    m_iPlayersCount = iPLAYERS_COUNT;
  }

  virtual void printGreeting()
  {
    cout << "Welcome to our wonderful game!" << endl;
  }

  // Einschub-Methode:
  virtual void initializeBoard() = 0;
  virtual void makeMove(const int iPLAYER) = 0;
  virtual bool gameFinished() = 0;
  virtual void printWinner() = 0;

  // Hook-Methode:
  virtual void takeADrink(const int iPLAYER) { }

public:
  // Schablonen-Methode
  void playOneGame()
  {
    printGreeting();
    initializeBoard();
    int i = 0;

    while (!gameFinished())
    {
      takeADrink(i); // Aufruf des Hook (standardmäßig leer)
      makeMove(i);
      i = (i + 1) % m_iPlayersCount;
    }

    printWinner();
  }
};

class Chess : public Game
{
protected:
  void initializeBoard()
  {
    // ...
  }
  void makeMove(const int iPLAYER)
  {
    cout << "Player " << iPLAYER << "'s turn in chess game" << endl;
    // ...
  }
  bool gameFinished()
  {
    // ...
  }
  void printWinner()
  {
    // ...
  }

public:
  Chess() : Game (2) { }
};

class Monopoly : public Game
{
protected:
  void printGreeting()
  {
    cout << "Welcome to monopoly!" << endl;
  }
  void initializeBoard()
  {
    // ...
  }
  void makeMove(const int iPLAYER)
  {
    cout << "Player " << iPLAYER << "'s turn in monopoly game" << endl;
    // ...
  }
  bool gameFinished()
  {
    // ...
  }
  void printWinner()
  {
    // ...
  }

public:
  Monopoly(const int iPLAYERS_COUNT) : Game(iPLAYERS_COUNT) { }
};

class DrinkersMonopoly : public Monopoly
{
protected:
  void printGreeting()
  {
    Monopoly::printGreeting();
    cout << "(The drinkers' version)" << endl;
  }
  void takeADrink(const int iPLAYER)
  {
    cout << "Player " << iPLAYER << " drinks a glass of whiskey" << endl;
  }

public:
  DrinkersMonopoly(const int iPLAYERS_COUNT) : Monopoly(iPLAYERS_COUNT) { }
};

int main(int iArgc, char* pa_Argv[])
{
  Game* p_MyGame;

  if (iArgc <= 1 || strcmp(pa_Argv[1], "Chess") == 0)
  {
    p_MyGame = new Chess();
  }
  else if (strcmp(pa_Argv[1], "Monopoly") == 0)
  {
    p_MyGame = new Monopoly(4);
  }
  else if (strcmp(pa_Argv[1], "DrinkersMonopoly") == 0)
  {
    p_MyGame = new DrinkersMonopoly(4);
  }
  else
  {
    cout << "Unknown game." << endl;
    return 1;
  }

  p_MyGame->playOneGame();
  delete p_MyGame;
  return 0;
}

Individual evidence

  1. Erich Gamma , Richard Helm , Ralph Johnson , John Vlissides : Design pattern . 5th edition. Addison-Wesley , 1996, ISBN 3-8273-1862-9 , pp. 366 .

Web links