The Template Method pattern defines the outline or skeleton of an operation, but leaves the specific steps involved to be defined by subclasses.
In other words, the Template Method pattern defines in what order certain steps should occur, but can optionally leave the specific details of those steps to be implemented by other classes. Whereas Factory Method did something similar with creating objects, Template Method does this with the behavior of those objects.
NOTE: This post is part of a series demonstrating software design patterns using C# and .NET. The patterns are taken from the book Design Patterns by the Gang of Four. Check out the other posts in this series!
The Rundown
- Type: Behavioral
- Useful? 4/5 (Very, with some caveats)
- Good For: Creating an outline of an algorithm but letting specific steps be implemented by other classes.
- Example Code: On GitHub
The Participants
- The AbstractClass defines a set of abstract operations which can (optionally) be implemented by ConcreteClass objects. It also implements a template method which controls the order in which those abstract operations occur.
- The ConcreteClass objects implement the operations defined by the AbstractClass.
A Delicious Example
To properly demo this design pattern, let's talk about something humanity has been doing for 30,000 years: baking bread.
There are easily hundreds of types of bread currently being made in the world, but each kind involves specific steps in order to make them. While acknowledging that this doesn't necessarily cover all kinds of bread that are possible to make, let's say that there are three basic steps in making bread:
- Mix the ingredients together.
- Bake the mixture.
- Slice the resulting bread.
We want to model a few different kinds of bread that all use this same pattern, which (no surprise) is a good fit for the Template Method design pattern.
First, let's create an AbstractClass Bread
which represents all breads we can bake:
/// <summary>
/// The AbstractClass participant which contains the template method.
/// </summary>
abstract class Bread
{
public abstract void MixIngredients();
public abstract void Bake();
public virtual void Slice()
{
Console.WriteLine("Slicing the " + GetType().Name + " bread!");
}
// The template method
public void Make()
{
MixIngredients();
Bake();
Slice();
}
}
Notice that the MixIngredients()
and Bake()
methods are abstract
, while the Slice()
method is virtual
. This is intentional: the method by which you slice bread is not likely to change depending on the kind of bread you make. Further, the Make()
method is the Template Method that gives this pattern its name.
Let's extend this example by implementing several ConcreteClass objects for different types of bread:
class TwelveGrain : Bread
{
public override void MixIngredients()
{
Console.WriteLine("Gathering Ingredients for 12-Grain Bread.");
}
public override void Bake()
{
Console.WriteLine("Baking the 12-Grain Bread. (25 minutes)");
}
}
class Sourdough : Bread
{
public override void MixIngredients()
{
Console.WriteLine("Gathering Ingredients for Sourdough Bread.");
}
public override void Bake()
{
Console.WriteLine("Baking the Sourdough Bread. (20 minutes)");
}
}
class WholeWheat : Bread
{
public override void MixIngredients()
{
Console.WriteLine("Gathering Ingredients for Whole Wheat Bread.");
}
public override void Bake()
{
Console.WriteLine("Baking the Whole Wheat Bread. (15 minutes)");
}
}
Once we've defined a few types of bread, we can simulate making them in our Main()
method, like so:
static void Main(string[] args)
{
Sourdough sourdough = new Sourdough();
sourdough.Make();
TwelveGrain twelveGrain = new TwelveGrain();
twelveGrain.Make();
WholeWheat wholeWheat = new WholeWheat();
wholeWheat.Make();
Console.ReadKey();
}
Template Method is (arguably) the simplest and most flexible of all the behavioral patterns.
Will I Ever Use This Pattern?
Almost certainly. I'd be willing to bet that most of you dear readers have already used this pattern and may not have known what it was called. This pattern is extremely common, flexible, and useful for many different applications and scenarios.
...However, it's not without problems. Jimmy Bogard explains:
"While some gravitate towards the Singleton pattern to abuse after they learn the GoF patterns, that wasn’t the case for me. Instead, I fell in love with the Template [Method] Pattern. But there’s a problem with [this] pattern as the golden hammer for every incidence of duplication we find in our application. The Template Method favors inheritance over composition."
And, to be fair, he's right. The Template Method pattern forces a class to inherit from a class rather than promoting object composition. If we're looking for strict-object-oriented design, Template Method could be better replaced by other patterns we've yet to cover, such as the Strategy pattern or the Command pattern.
But I'm not willing to go as far as saying "don't use Template Method." As with all the other patterns, their applications depend on what problem you need to solve and how you want to do so. Template Method is prone to over-use, so be careful with it.
Summary
The Template Method design pattern allows for an object to set up a skeleton of an algorithm but leave the implementation details up to the concrete classes to implement.
Happy Coding!