The template pattern defines an algorithm in a superclass and makes subclasses responsible for some of the logic. The algorithm is a “template” in the superclass. Subclasses cannot override the template algorithm or any of the non abstract superclass methods. This pattern protects against dependency rot, code duplication and makes use of “inversion of control” principle (sometimes referred to as the Hollywood principle: “Don’t call us, we’ll call you.”). This pattern is similar to the Strategy Pattern as it encapsulates algorithms and delegates responsibilities to implementations / clients.
Example Problem:
Consider the some of the similarities of brewing beer and wine. Both include adding ingredients, fermenting, filtering and bottling but the processes differ between the two (the processes here are greatly simplified for this example). Here’s an example of a Wine
and Beer
implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public class Wine { void prepareWine() { addGrapeJuice(); ferment(); filter(); bottle(); } private void bottle() { } private void filter() { } private void ferment() { } private void addGrapeJuice() { } } public class Wine { void prepareBeer() { addHops(); addYeastAndFerment(); filter(); bottle(); } private void bottle() { } private void filter() { } private void addYeastAndFerment() { } private void addHops() { } } |
The methods are left blank for simplicity. Consider the bottle()
and filter()
methods share the same code while the other methods are specific to their implementations. Let’s implement the Template Pattern:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public abstract class AlcoholicBeverage { final void brew() { addIngredients(); ferment(); filter(); bottle(); } abstract void addIngredients(); abstract void ferment(); void filter() { } void bottle() { } } |
What have we done here? We’ve made the brew()
method final so subclasses are unable to override it. This is the encapsulated “template” algorithm. The two methods filter()
and bottle()
have the same logic so they live in the superclass. The methods addIngredients()
and ferment()
have logic specific to their implementations so they’re declared abstract. The subclasses are responsible for overriding and defining the logic.
Here’s how the implementations of Beer
and Wine look now:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public class Wine extends AlcoholicBeverage { @Override void addIngredients() { System.out.println("Add grapes."); } @Override void ferment() { System.out.println("Add cultered yeast."); } } public class Beer extends AlcoholicBeverage { @Override void addIngredients() { System.out.println("Add hops"); } @Override void ferment() { System.out.println("Add yeast to fermenter"); } } |
Client Application
Here’s an example of the Wine
and Beer
classes in action.
1 2 3 4 5 | Wine wine = new Wine(); Beer beer = new Beer(); wine.brew(); beer.brew(); |
Hooks
Hooks are empty superclass methods that don’t need to be overridden. In this case hook methods can be added to the algorithm template. The hook will be invoked on all subclasses overriding the method. For example:
1 2 3 4 5 6 7 8 9 10 11 | final void brew() { addIngredients(); ferment(); fermentingComplete(); filter(); bottle(); } void fermentingComplete() { } |
Summary
The template pattern defines an algorithms steps letting subclasses define some of the steps. It aims to keep decision making logic in high level modules reducing dependency rot. This pattern has some similarity to the Factory Pattern and the Strategy Pattern. Implementation of the template pattern throughout Javas source code exists but can greatly differ from this example.