Fundamentals 14 min read

Template Method Pattern: Fix the Skeleton, Vary the Steps

The article explains the Template Method design pattern, showing how to lock a fixed process skeleton in a parent class while delegating variable steps to subclasses, contrasting it with the Strategy pattern and illustrating its use in real‑world frameworks.

Dabaoshi
Dabaoshi
Dabaoshi
Template Method Pattern: Fix the Skeleton, Vary the Steps

Why the Template Method?

When multiple order‑processing classes share the same overall workflow—validation, amount calculation, inventory deduction, persistence, and notification—developers often copy‑paste the code. This leads to massive duplication, fragile ordering, and the need to modify many places when a common step changes.

Duplicated Order Services (Problem)

public class NormalOrderService {
    public void placeOrder(Order order) {
        // 1. 校验
        if (order.getUserId() <= 0) throw new RuntimeException("用户非法");
        // 2. 计算金额
        double amount = order.getItems().stream().mapToDouble(Item::getPrice).sum();
        // 3. 扣减库存
        inventory.deduct(order);
        // 4. 保存订单
        repository.save(order);
        // 5. 发送通知
        notify.send(order.getUserId(), "下单成功");
    }
}

For a group‑buy order the same skeleton is reused, but two steps differ (extra group check and group pricing), so developers copy the whole class and modify only those parts, repeating the unchanged steps.

Template Method Solution

Define an abstract base class that contains a final template method describing the fixed workflow. Variable steps are declared abstract, forcing subclasses to implement them. Optional steps are provided as hook methods with default (often empty) implementations.

public abstract class AbstractOrderService {
    // Template method: fixed skeleton, cannot be overridden
    public final void placeOrder(Order order) {
        validate(order);                 // variable
        double amount = calcAmount(order); // variable
        inventory.deduct(order);          // fixed
        repository.save(order);           // fixed
        if (needNotify()) {
            sendNotify(order);           // variable (hook)
        }
        afterPlaceOrder(order);           // optional hook
    }

    protected abstract void validate(Order order);
    protected abstract double calcAmount(Order order);
    protected abstract void sendNotify(Order order);

    // Hook methods with default behavior
    protected boolean needNotify() { return true; }
    protected void afterPlaceOrder(Order order) { }
}

Concrete services only implement the abstract steps:

public class NormalOrderService extends AbstractOrderService {
    protected void validate(Order order) {
        if (order.getUserId() <= 0) throw new RuntimeException("用户非法");
    }
    protected double calcAmount(Order order) {
        return order.getItems().stream().mapToDouble(Item::getPrice).sum();
    }
    protected void sendNotify(Order order) {
        notify.send(order.getUserId(), "下单成功");
    }
}

public class GroupOrderService extends AbstractOrderService {
    protected void validate(Order order) {
        if (order.getUserId() <= 0) throw new RuntimeException("用户非法");
        if (!checkGroupFormed(order)) throw new RuntimeException("未成团");
    }
    protected double calcAmount(Order order) {
        return calcGroupPrice(order); // group price
    }
    protected void sendNotify(Order order) {
        notify.send(order.getUserId(), "拼团下单成功");
    }
}

Now the fixed steps (inventory deduction, persistence) exist only once in the abstract class, and the order of steps cannot be altered because placeOrder is final. Subclasses merely fill in the blanks.

Key Mechanisms

Hook Methods : optional overridable methods (e.g., needNotify, afterPlaceOrder) that provide flexibility without breaking the skeleton.

Final Template Method : guarantees the workflow cannot be changed by subclasses, preserving order and completeness.

Real‑World Appearances

AbstractList

/ AbstractMap in the JDK: the skeleton of collection operations is fixed, while core methods like get(index) or size() are abstract. HttpServlet.service(): defines the request‑handling skeleton; subclasses override doGet, doPost, etc.

Spring’s JdbcTemplate, RestTemplate, RedisTemplate: the template handles resource acquisition, exception handling, and cleanup, leaving the actual SQL or processing logic to callbacks.

Spring’s lifecycle callbacks such as AbstractApplicationContext.refresh() follow the same pattern.

Template Method vs. Strategy

Inheritance vs. Composition : Template Method uses subclassing to vary individual steps; Strategy uses composition to replace the whole algorithm.

Granularity : Template Method varies only specific steps; Strategy swaps the entire behavior.

Flexibility : Template Method is fixed at compile time; Strategy can be switched at runtime.

Use Template Method when many classes share a stable process skeleton but differ in a few steps, when you want to centralize common logic and enforce ordering. Avoid it when there is no common skeleton, when dynamic runtime switching is required, or when only a single implementation exists.

Bottom Line

The Template Method pattern separates a fixed workflow (locked with final) from variable steps (abstract methods) and optional hooks, embodying the Hollywood principle: the framework calls back into the subclass. It is a cornerstone of framework design, evident in JDK abstract classes, servlet processing, and Spring templates, and complements the Strategy pattern, which is preferable for fully interchangeable algorithms.

Template Method diagram
Template Method diagram
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaDesign PatternTemplate MethodInheritanceFinalHook Method
Dabaoshi
Written by

Dabaoshi

Practical utilities

0 followers
Reader feedback

How this landed with the community

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.