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.

In Case of Analysis Paralysis, Just Start!

When tackling a problem or learning a new technology, we coders can experience “Analysis Paralysis” – getting overwhelmed by the enormity of the entire task, or trying to learn everything at once, and not creating something because I “just have to learn just one more thing or figure out every possible scenario before I can start.”

We have analytical minds that are constantly trying to prepare for every possible issue.

              Wife: “Honey, I think I might be pregnant”

              Coder: “In 19 years, how is our future son Bilbo going to get into Harvard, and where will we get the tuition?….”

              Wife: “Maybe we should find out if I’m pregnant first…”

We’ve already given the possible zygote a gender, a name that will get him bullied at school, have set his goals for him to include an Ivy League college, and are worried about the cost of it 19 years in the future – and we’re not even sure that she’s pregnant yet!

We love to code, but the paralysis can hold us back from creating anything. Imposter syndrome, table for one!

I am part of a group called DevBetter (check them out at www.devbetter.com). When this topic comes up in its various forms, the advice is very simple and straightforward: when the paralysis sets in, “just start”.

Do “something” – pick something small, and just create. You can iterate on it and make it better.

Console.WriteLine(“Hello World!”); can be refactored. An idea in your head can’t.

In DevBetter, we are also encouraged to blog about what we’ve learned.

I want to learn Blazor Web Assembly – I’ve read a little about it, and have had discussions in the DevBetter group, but I have little experience with it. I have an idea for a website, and I’m just going to dive in, and learn as I go. It will be a vehicle for me to learn Blazor, and I will be sharing my journey as I go.

Stay tuned for my next post – the “Drog.”

To Format Or Interpolate

In a devBetter discussion, a question was raised regarding composing strings: “Why would a developer use String.Format, when string interpolation seems much cleaner and easier to use?”

In case you are not familiar with StringFormat or string interpolation, here’s a code sample that demonstrates using them to compose a string:

Variables x, y, and z will all contain the same string value (“My name is Rick and my shoe size is 9.5”), but you can see how each approach used becomes progressively easier to read and understand. String interpolation is like a shorthand for a call to String.Format.

We’ll soon see that behind the scenes, string interpolation actually compiles down into a call to String.Format, but let’s look at String.Format, first.

String.Format evaluates its format parameter at runtime. Consider the following code that accepts a format string from the console, and displays the result of String.Format():

Here’s a sample of the console output:

Enter a format that contains {0} and {1}:
You entered: Name {0} Shoe {1}
Result: Name Rick Shoe 9
Enter a format that contains {0} and {1}:
You entered: Shoe {1} Name {0}
Result: Shoe 9 Name Rick

In contrast, interpolation builds the format expression at compile time.

Let’s see what happens when we try to use string interpolation like String.Format:

Why don’t we get the same results? The reason is that the compiler “hard codes” the interpolated format.

Let’s look at how the compiler compiles the following string interpolation:

Here is the IL generated by the compiler.

Compared to our “changeable” format in the while(true) loop, the format expression (the line starting with IL_000A) is “baked in”: the string “My name is …” will never change.

Both String.Format and string interpolation are valuable tools in our toolbelt. I use string interpolation most of the time (since it was added to C# in version 6), but String.Format can be very useful when you need to have configurable formats.

Timing Code With WatchMe, Part 1

It is often helpful to track the timing of sections of code. One way to accomplish this is to put messages directly into your code that displays the timings.

DISCLAIMER: this article is not about performance tuning of code, it is about simplifying incorporating timing during development.

Let’s take a look at a method that given an integer, returns the sum of all integers up to the passed integer, and has code to display timings.

If you call this method and pass 1,000,000 you will see output similar to the following:

SumUpTo started at  2019-12-22 20:51:02.420 UTC
SumUpTo – i = 0 0ms

SumUpTo – i = 1000000 6.9798ms
SumUpTo finished 6.9798ms

While helpful in determining where timing issues lie in the code, it has side effects:

  1. It adds several lines of code that have nothing to do with the behavior of the method – there are ten lines of code that do not relate to the calculation of the return value, which will impact timing. It may not be a significant impact, but there is an impact. The less we do, the less impact on timing we will experience.
  2. These extra lines of code are spread throughout the routine:
    • a start time is captured at the beginning
    • an elapsed time is calculated at several places
    • a periodic status message every 100,000 iterations of the loop
    • a summary message at the end, showing the elapsed time.
  3. Capturing elapsed time has accuracy and thread safety issues.  – there is a class in the .NET framework called Stopwatch that can help with this, we’ll see in a later part of this series.
  4. Removing the timing code can be tedious.

One approach I have used to make this cleaner and to address these issues is to simply wrap what I want to measure with a “using” statement that instantiates an instance of a class called WatchMe. Here is the same method, re-written using the WatchMe class:

Much cleaner, in fact if we take out the “if” statement surrounding the call to ShowMessage, the using takes care of all of the timings.

The WatchMe class takes care of the following:

  1. Takes advantage of the IDisposable interface to capture finish elapsed time, and logging the final elapsed time message.
  2. Uses the Stopwatch class to handle some of the issues with capturing elapsed time.
  3. Holds on to the title of what is being watched (e.g. SumUpTo)
  4. Captures the DateTime start variable used later to calculate the various elapsed times.
  5. Automatically displays the start and finished messages
  6. Simplifies the interim message to only the message: the title and elapsed times are added to the message automatically.

We’ll take a look at the WatchMe class in part 2 of this series.