Understanding Template Method Pattern made Easy

In this detailed article, let's understand what is a Template Method design pattern, how to use it and the differences between Template Method and Strategy

What is Template Method pattern?

Template Method is a Behavioral Design Pattern.

It is used when you need to create multiple variants of an algorithm with large parts of code shared between the variants.

It states as follows – “define an algorithm as a series of operations and let child classes customize these operations as required.

When to use Template Method?

Imagine you are building a logging component for your application, and you have first completed an implementation that writes to Console. It follows a couple of steps such as – Fetching Configuration, Creating a Logger and then Writing the message.

As the application grows in size, you are now asked to switch to a database logging mechanism where you need to perform almost similar steps, but the target is now a table in the database instead of Console. You put a switch condition and move forward.

After a few days you now wish to write another implementation of logging where you can write to a log file instead of the existing options – Console and Database.

In all these cases, you may find copying over the same code and steps again and again for a very little change in the implementation. Also, you are modifying the existing system for this.

This is where you can use a Template Method, to break down your algorithm into a series of calling functions and let the variations decide which part of these series of steps be reused or replaced.

Template Method and Hooks

Template Method can also help you create “Hooks”, which are optional pieces of steps that have a default implementation in place. The variations can either be replaced with their own specialties or can leave it as it is for the defaults to work.

How to implement Template Method Design Pattern?

You can implement Template Method using the following steps –

  1. Split the algorithm into multiple methods inside an abstract base class. This base class also contains common code which is shared among the variations of the algorithm.
  2. All the variants of the algorithms are represented by derived concrete classes that extend this base abstract class.
    • These concrete classes also contain their own implementation of the parts which are specific to them.
    • They can also use the common code that is present in the base class by means of inheritance.
  3. A Template method inside the base class is the starting point, which calls the sequence of operations involved with the algorithm.
  4. A calling client receives an instance of the variation it requires as a type of the abstract base class and calls the Template method. Clients can call the implementation it requires without having to think much about the internals.

Implementing Template Method with a C# Example

The below code explains how to implement the Template Method for the Logger example, mentioned above.

namespace DesignPatternsExamples.Patterns.Behavioral;

public abstract class AbstractLogger
{
    /// <summary>
    /// Template Method - Defines Steps to write Log
    /// </summary>
    /// <param name="message"></param>
    public void WriteLog(string message)
    {
        // Algorithm as a series of calling functions

        // step 1 - Load configuration
        var config = this.LoadConfiguration();

        // step 2 - get logger
        var logger = this.GetLogger(config);

        // step 3 - write to logger
        WriteToLogger(logger, message);
    }

    /// <summary>
    /// Hook - Optional method
    /// Which can be overridden by any custom implementation
    /// </summary>
    /// <returns></returns>
    protected virtual string LoadConfiguration()
    {
        Console.WriteLine("Load configuration from base class");
        return "Some Configuration Setting";
    }

    /// <summary>
    /// Required Implementation - Variation in Algorithms
    /// </summary>
    /// <param name="configurationSetting"></param>
    /// <returns></returns>
    protected abstract string GetLogger(string configurationSetting);

    /// <summary>
    /// Common Functionality - Shared code among variations
    /// </summary>
    /// <param name="logger"></param>
    /// <param name="message"></param>
    protected void WriteToLogger(string logger, string message)
    {
        Console.WriteLine("Writing input message {0} to logger {1}", message, logger);
    }
}

/// <summary>
/// Algorithm 1 - Console Logger Variation
/// </summary>
public class ConsoleLogger : AbstractLogger
{
    protected override string GetLogger(string configurationSetting)
    {
        Console.WriteLine(
            "Get Console Logger for the loaded configuration {0}",
            configurationSetting
        );
        return "Some logger";
    }
}

/// <summary>
/// Algorithm 2 - Database Logger Variation
/// </summary>
public class DatabaseLogger : AbstractLogger
{
    protected override string LoadConfiguration()
    {
        Console.WriteLine("Load Database Settings for the Configuration.");
        return "Some configuration with database setting";
    }

    protected override string GetLogger(string configurationSetting)
    {
        Console.WriteLine(
            "Get Database Logger for the loaded configuration {0}",
            configurationSetting
        );
        return "Some logger";
    }
}

/// <summary>
/// A Calling Client - does something while calling the Template method
/// as a part of its own business logic
/// It doesn't need to know which of the variation is being called in place
/// of the AbstractLogger
/// </summary>
public class TemplateMethodClient
{
    AbstractLogger _logger;

    public TemplateMethodClient(AbstractLogger logger)
    {
        _logger = logger;
    }

    public void CallingClient()
    {
        // some other functionality
        _logger.WriteLog("Writing Log from Calling Client.");
    }
}

When you call the TemplateMethodClient, you just need to pass an implementation of your requirement (DatabaseLogger or ConsoleLogger) and all the steps are executed just fine.

var dbLogger = new DesignPatternsExamples.Patterns.Behavioral.DatabaseLogger();
var consoleLogger = new DesignPatternsExamples.Patterns.Behavioral.ConsoleLogger();

Console.WriteLine();
Console.WriteLine();

var templateMethodExampleWithDbLogger = 
    new DesignPatternsExamples.Patterns.Behavioral.TemplateMethodClient(dbLogger);

templateMethodExampleWithDbLogger.CallingClient();

Console.WriteLine();
Console.WriteLine();

var templateMethodExampleWithConsoleLogger = 
    new DesignPatternsExamples.Patterns.Behavioral.TemplateMethodClient(consoleLogger);

templateMethodExampleWithConsoleLogger.CallingClient();

Output of the implementation

Difference between template method and strategy pattern

On the face value, both Template Method and Strategy patterns solve the same problem – a family of algorithms, and showing variations.

But they differ in the way they are implemented and designed.

Template Method PatternStrategy Pattern
Behavioral PatternBehavioral Pattern
Algorithm is broken down into series of steps and are selectively overriden by the variationsAlgorithm is implemented specific to variations but are made substitutable to one another
Based on InheritanceBased on Composition – Classes and Objects
Variations extend a common base template with shared codeVariations implement a common template with own implementations
Template method is static – binding happens at compile timeStrategy pattern is dynamic – variations are switched at runtime
Runs at class levelRuns at object level
differences between template method and strategy design pattern

Template Method is based on Inheritance. It is similar to Strategy pattern but Strategy pattern is based on composition – it works on with objects based on an abstract strategy.

Template method is Static, binding happens at compile-time while Strategy switches on the variants based on client choice during runtime. Template method runs at class level. Strategy at object level.


Buy Me A Coffee

Found this article helpful? Please consider supporting!

Ram
Ram

I'm a full-stack developer and a software enthusiast who likes to play around with cloud and tech stack out of curiosity. You can connect with me on Medium, Twitter or LinkedIn.

Leave a Reply

Your email address will not be published. Required fields are marked *