Fundamentals 12 min read

Understanding the Template Method Design Pattern with Java Examples

This article explains the Template Method design pattern, illustrating its structure with Java code examples—including an abstract Drinks class, concrete Tea and Coffee subclasses, hook methods, and real‑world usages in JDK sorting and Spring's application context—while highlighting advantages, drawbacks, and typical scenarios.

Full-Stack Internet Architecture
Full-Stack Internet Architecture
Full-Stack Internet Architecture
Understanding the Template Method Design Pattern with Java Examples

The Template Method pattern defines an algorithm's skeleton in a superclass while allowing subclasses to override specific steps. This article introduces the pattern, shows why it is common in concurrent programming and framework design, and demonstrates its use with clear Java examples.

Problem scenario : Using a beverage shop analogy, the author shows that different drinks share common steps (boiling water, pouring into a cup) but differ in brewing and condiment addition. An abstract parent class can encapsulate the invariant steps.

Abstract class implementation :

public abstract class Drinks {
    void boilWater() {
        System.out.println("将水煮沸");
    }
    abstract void brew();
    void pourInCup() {
        System.out.println("倒入杯子");
    }
    abstract void addCondiments();
    public final void makingDrinks() {
        //热水
        boilWater();
        //冲泡
        brew();
        //倒进杯子
        pourInCup();
        //加料
        addCondiments();
    }
}

Concrete subclasses implement the abstract steps:

public class Tea extends Drinks {
    @Override
    void brew() {
        System.out.println("冲茶叶");
    }
    @Override
    void addCondiments() {
        System.out.println("加柠檬片");
    }
}
public class Coffee extends Drinks {
    @Override
    void brew() {
        System.out.println("冲咖啡粉");
    }
    @Override
    void addCondiments() {
        System.out.println("加奶加糖");
    }
}

Testing the template:

public static void main(String[] args) {
    Drinks coffee = new Coffee();
    coffee.makingDrinks();
    System.out.println();
    Drinks tea = new Tea();
    tea.makingDrinks();
}

The article then introduces hook methods to make optional steps configurable. By adding a customerLike() method that can be overridden, subclasses decide whether to add condiments.

public abstract class Drinks {
    // ... previous methods ...
    public final void makingDrinks() {
        boilWater();
        brew();
        pourInCup();
        //如果顾客需要,才加料
        if (customerLike()) {
            addCondiments();
        }
    }
    //默认返回 true 的钩子方法
    boolean customerLike() {
        return true;
    }
}
public class Coffee extends Drinks {
    @Override
    void brew() {
        System.out.println("冲咖啡粉");
    }
    @Override
    void addCondiments() {
        System.out.println("加奶加糖");
    }
    //覆盖钩子,询问用户是否加料
    boolean customerLike() {
        String answer = getUserInput();
        if (answer.toLowerCase().startsWith("y")) {
            return true;
        } else {
            return false;
        }
    }
    private String getUserInput() {
        String answer = null;
        System.out.println("您想要加奶加糖吗?输入 YES 或 NO");
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        try {
            answer = reader.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (answer == null) {
            return "no";
        }
        return answer;
    }
}

Real‑world examples :

In the JDK, Comparable and sorting utilities use a template‑method‑like approach: the compareTo() method is supplied by the user, while Arrays.sort() or Collections.sort() provide the overall algorithm.

public int compareTo(Object o) {
    Coffee coffee = (Coffee) o;
    if (this.price < coffee.price) {
        return -1;
    } else if (this.price == coffee.price) {
        return 0;
    } else {
        return 1;
    }
}
public static void main(String[] args) {
    Coffee[] coffees = {new Coffee("星冰乐", 38), new Coffee("拿铁", 32), new Coffee("摩卡", 35)};
    Arrays.sort(coffees);
    for (Coffee coffee1 : coffees) {
        System.out.println(coffee1);
    }
}

Spring's AbstractApplicationContext.refresh() method is another classic template method, defining the overall container startup flow while delegating specific steps (e.g., getBeanFactory() , refreshBeanFactory() ) to subclasses and providing hook points such as postProcessBeanFactory() .

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
    @Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
            prepareBeanFactory(beanFactory);
            postProcessBeanFactory(beanFactory);
            invokeBeanFactoryPostProcessors(beanFactory);
            registerBeanPostProcessors(beanFactory);
            initMessageSource();
            initApplicationEventMulticaster();
            onRefresh();
            registerListeners();
            finishBeanFactoryInitialization(beanFactory);
            finishRefresh();
        }
    }
    // two abstract methods
    public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
    protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
    // hook method (default empty)
    protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {}
}

Spring's concrete context classes (e.g., ClassPathXmlApplicationContext , AnnotationConfigApplicationContext ) inherit this template and may override hook methods like onRefresh() to add custom behavior.

public abstract class AbstractRefreshableWebApplicationContext extends ... {
    @Override
    protected void onRefresh() {
        this.themeSource = UiApplicationContextUtils.initThemeSource(this);
    }
}

Summary : The Template Method pattern encapsulates invariant parts of an algorithm in a superclass, forces subclasses to implement variable steps, and optionally provides hook methods for further customization. Its advantages are code reuse, easier maintenance, and clear control flow, while its drawbacks include increased class count.

Typical use cases are when multiple subclasses share a common workflow with minor variations, especially for complex or critical algorithms.

JavaSpringdesign patternTemplate MethodHook Method
Full-Stack Internet Architecture
Written by

Full-Stack Internet Architecture

Introducing full-stack Internet architecture technologies centered on Java

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.