Spare the Algorithm, Spoil the Steps: The Template Method Pattern

       

In this post, I will present the Template Method pattern from Design Patterns.

The Template Method is a great pattern to start your journey into Design Patterns: if you have ever designed an abstract class, it will feel familiar.

The Template Method pattern defines an algorithm in a method (the “template” method). The steps of the algorithm are expressed as methods called from the Template Method. To change the behavior of the Template Method in subclasses, you modify (override) the “step” methods, leaving the algorithm intact.

Care For a Game of Cards?

Let’s design a class that is the basis for creating card games.

Card games have a familiar set of steps.

  1. You get a deck of cards.
  2. The cards are shuffled.
  3. Some games perform an extra step before dealing the cards: for example, placing a bet.
  4. Cards are dealt to the players.
  5. Some games perform an extra step after dealing the cards: for example, after dealing cards, each player takes one card from their hand and gives it to the player on their right.
  6. The game is started.

This algorithm could be expressed in a method named “Play” that calls each of these steps:

public abstract class CardGame
{   
protected List<Card> Deck = new List<Card>();
public void Play()   
{     
  CreateDeck();
    ShuffleDeck();
    BeforeDeal();     
  DealCards(); 
   AfterDeal();       
StartGame();   
}   
protected abstract void CreateDeck();   
protected virtual void BeforeDeal() {  }   
protected virtual void ShuffleDeck()   
{       
// shuffle list of cards into random order       
for (int card = 0; card < Deck.Count; card++) 
  {       
    // … 
  }   
}   
protected abstract void DealCards();   
protected virtual void AfterDeal() {  }   
protected abstract void StartGame();
}


Notice that the Play() method cannot be overridden (it is not virtual or abstract), but the step methods called from it are all overridable (abstract/virtual). This is to prevent changing the template: it encourages you to override the steps to change behavior while keeping the algorithm intact. Subclasses of CardGame are going to follow the same steps in the same order every time Play() is called: the deck will always be created first, then shuffled, and so on. The subclasses will have the opportunity to modify the behavior of one or more steps in the algorithm.

Also notice that the step methods are not public: nothing outside of CardGame should be aware of the steps.

Step Method Categories

This pattern categorizes the possible step methods that are called from the Template Method.

Factory Methods

Factory Method is another Design Pattern. A Factory Method is a placeholder for the creation of something. CreateDeck() is a placeholder for creating the collection of cards in the deck.

A Poker game could create a standard list of playing cards:

    protected override void CreateDeck()
    {        
        Deck = new List<Card>{new Card("2H"),new Card("3H")…}
    }

A Concentration game could override CreateDeck() to fill the deck with picture cards.

    protected override void CreateDeck()
    {        
        Deck = new List<Card>{new Card("Triangle"),new Card("Square")…};
    }

The names of Factory Methods often follow a convention of prefixing the method name with “Create.”

Abstract Operations

These methods are placeholders for behavior that is specific to the subclass. StartGame() is an example of an abstract operation. StartGame() will be unique in every subclass – PokerCardGame will start Poker, PinochleCardGame will start Pinochle, and so on. DealCards is another example of an abstract operation: each game will deal cards differently.

Concrete Virtual Class Operations

These are methods that can be defined in the abstract class to hold behavior that is reusable in subclasses. ShuffleDeck() is an example of this. In the example above, Deck is defined as List<Card>. Shuffling a list of cards is straightforward: just randomly sort the cards. It is given a default behavior in the abstract class to sort the cards. ShuffleDeck() is marked as virtual, so that if a game requires different method of shuffling, you can override the way a deck is shuffled.

Hook Operations

Hook Operations are methods that are placeholders for behaviors that are by default empty. In our use case for card games, a certain percentage of games need to perform a behavior at a certain location in the algorithm: not all the time, but enough that you need to provide a place to include this behavior. BeforeDeal and AfterDeal are examples of Hook Methods.

Summary

The Template Method pattern is a good example of one of the SOLID principles: the Open-Closed principle says that code should be open to change but closed to modification. The Template Method allows the steps of the algorithm to change, without modfiying the order of the steps in the algorithm.