Template Method pattern: Make your application plug-in-able

Template Method is one of the behavioral patterns which means you can manage the code to behave differently based on our logic. Generally, we can do interesting things with these kinds of patterns another popular one is Strategy Pattern

The template method is used to define a skeleton of an algorithm or operations in a base class (better to be an abstract class) and other subset classes can follow the steps or even customize them. That said, we have a pipeline (including several steps) that other classes should follow.

Let's refactor a codebase and see how we can leverage this pattern, assuming we have an interface called IPizza that is responsible for making pizza which I love:)

public interface IPizza
{
    Task MakePizza();
}

We have a method to make it and here is the implementation:

public class MargheritaPizza : IPizza
{
    public async Task MakePizza()
    {
        Console.WriteLine("Preparing the dough.");

        await Task.Delay(1000);

        Console.WriteLine("Baking the pizza.");

        await Task.Delay(1000);

        Console.WriteLine("Add toppings: tomato sauce, mozzarella, and basil.");

        await Task.Delay(1000);

        Console.WriteLine("Cutting the pizza.");

        await Task.Delay(1000);

        Console.WriteLine("Serving the pizza.");
    }
}

Ok, this is the code we already have, the requirement was for making Margherita and all is good and working but there is a new feature for making Pepperoni pizza (my favorite one). What can we do here? One is to create another implementation of IPizza and name it PepperoniPizza and write some code again, right? but let's think a little about the big picture!

Our feature is making pizza and we know there are some steps to do that are common for almost all pizza types like preparing dough or cutting pizza and serving it. So better thought is why not exclude these repetitive steps from both classes and just reuse it, seems they name the approach as DRY 😃

Image Source: geeksforgeeks.org

So, we already have a pipeline (or template) where some of the steps are common and the same for all pizzas but also have some steps that should be implemented by subclasses and they decide how to do their jobs. Thus, let's create a base class and define our template method!

public abstract class PizzaBase
{
    // Template method
    public async Task MakePizza()
    {
        // Step 1
        PrepareDough();
        // Step 2
        await AddToppings();
        // Step 3
        await AddExtraIngredients();
        // Step 4
        Bake();
        // Step 5
        Cut();
        // Step 6
        Serve();
    }

    protected virtual void PrepareDough()
    {
        Console.WriteLine("Preparing the dough.");
    }

    protected abstract Task AddToppings();
    protected virtual Task AddExtraIngredients() => Task.CompletedTask;

    protected virtual void Bake()
    {
        Console.WriteLine("Baking the pizza.");
    }

    protected virtual void Cut()
    {
        Console.WriteLine("Cutting the pizza.");
    }

    protected virtual void Serve()
    {
        Console.WriteLine("Serving the pizza.");
    }
}

I already separate the logic for each step into a method for a better understanding of the flow, in most of the other guides, the flow and steps are defined in the base class constructor which I think isn't a good idea and we can't get benefits of async/await pattern. By the nature of abstract classes and methods, they can override and change their behavior in subclasses. So, we know steps: 1, 4, 5, 6 are the same for pizza types but steps 2, 3 can be different per pizza type.

Now, let's refactor the MargheritaPizza class as below:

public class MargheritaPizza : PizzaBase, IPizza
{
    protected override async Task AddToppings()
    {
        await Task.Delay(1000);
        Console.WriteLine("Add toppings: tomato sauce, mozzarella, and basil.");
    }
}

Much less! How about the new pizza type for Pepperoni? Our restaurant wants to create a special pepperoni by adding extra cheese so now you grasp why we have a method called AddExtraIngredients If you check the base class there is a default implementation that is doing nothing means every subclass can override it or not.

public class PepperoniPizza : PizzaBase, IPizza
{
    protected override async Task AddToppings()
    {
        await Task.Delay(1000);
        Console.WriteLine("Adding tomato sauce, mozzarella, and pepperoni.");
    }

    protected override async Task AddExtraIngredients()
    {
        await Task.Delay(1000);
        Console.WriteLine("Adding more cheese to make it more delicious");
    }
}

In this way, subclasses only need to care about the actual logic, not common things!

You can see the Template Method as HTML page template where we have lots of common tags but also there are some placeholders to be filled by the caller.

When not to use?

Well, here we were talking about steps and structure that are able to be separated so if the steps are coupled and need to put many if/else better to not use this pattern, another one is having more abstraction in the code base which may cause a little overhead and more complex code navigation.

Thanks and Code Simple!

1
An error has occurred. This application may no longer respond until reloaded. Reload x