Template Method Design Pattern in Java
Follow a fixed algorithm while allowing subclasses to customize selected steps.
What is Template Method?
The Template Method pattern defines the overall steps of an algorithm in a superclass. Subclasses can change specific steps without changing the order of the process. Think of preparing coffee or tea: boiling water and pouring into a cup are common steps, while adding coffee grounds, tea leaves, sugar, milk, or lemon is different. This gives beginners a simple way to understand code reuse and controlled customization.
Beginner-friendly Java example
Focus on the roles in the example first. The pattern becomes easier when you can identify the sender, receiver, context, state, or strategy involved.
// Step 1: Define the abstract class
abstract class Beverage {
// Template method: the order of steps cannot change
public final void brew() {
boilWater();
addIngredients();
pourInCup();
// Hook method: subclasses may customize this optional step
if (customerWantsCondiments()) {
addCondiments();
}
}
// Steps that subclasses must provide
protected abstract void addIngredients();
protected abstract void addCondiments();
// Common steps shared by every beverage
protected void boilWater() {
System.out.println("Boiling water");
}
protected void pourInCup() {
System.out.println("Pouring into cup");
}
// Optional hook with a default behavior
protected boolean customerWantsCondiments() {
return true;
}
}
// Step 2: Implement concrete beverage classes
class Coffee extends Beverage {
@Override
protected void addIngredients() {
System.out.println("Adding coffee grounds");
}
@Override
protected void addCondiments() {
System.out.println("Adding sugar and milk");
}
@Override
protected boolean customerWantsCondiments() {
// In a real application, ask the customer for their preference
return true;
}
}
class Tea extends Beverage {
@Override
protected void addIngredients() {
System.out.println("Adding tea leaves");
}
@Override
protected void addCondiments() {
System.out.println("Adding lemon");
}
}
// Step 3: Test the implementation
public class Main {
public static void main(String[] args) {
Beverage coffee = new Coffee();
Beverage tea = new Tea();
System.out.println("Making Coffee:");
coffee.brew();
System.out.println("\nMaking Tea:");
tea.brew();
}
}Benefits and trade-offs
Benefits
- Keeps responsibilities focused.
- Reduces conditional and tightly coupled code.
- Makes behavior easier to extend and test.
Trade-offs
- Can introduce extra objects and interfaces.
- Too many small classes can make simple logic harder to follow.
Real-time use cases
- Beverage preparation workflows
- CSV and JSON import processes
- Report generation
- Test setup and teardown
- Document processing
Key takeawayFollow a fixed algorithm while allowing subclasses to customize selected steps. Use it when behavior or communication is changing faster than the objects themselves.
