Behavioral Design Pattern · Java

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.