Skip to main content

Template Method Pattern

A behavioral design pattern that defines the skeleton of an algorithm in a base class method and defers specific steps to subclasses, letting them override parts without changing the algorithm's structure.

What Problem Does It Solve?​

You have a data export pipeline: load data from the source, transform it, write it to the destination. You need two flavors: CSV export and JSON export. The overall flow is identical — only the transform and write steps differ.

Without Template Method, both CsvExporter and JsonExporter each implement the full pipeline — duplicating load logic, error handling, and cleanup. Change the load logic, and you update both classes. Add a third format, and you duplicate once more.

The Template Method pattern solves this by putting the invariant skeleton (loadData() → transform() → write()) in a base class, making transform() and write() abstract. Subclasses implement only what varies.

What Is It?​

The Template Method pattern has two participants:

RoleDescription
AbstractClassDefines the templateMethod() that calls abstract or hook methods in a fixed sequence
ConcreteClassExtends AbstractClass; overrides abstract steps with a specific implementation

Two types of methods in the template:

  • Abstract steps — must be overridden by subclasses (vary by subclass).
  • Hook methods — have a default (often empty) implementation; subclasses may override to add optional behavior.

How It Works​

Fixed steps (green) run the same for all subclasses. Abstract steps (blue) must be implemented per subclass. Hook methods are optionally overridden.

Code Examples​

Data Exporter​

// ── AbstractClass — defines the template ─────────────────────────────
public abstract class DataExporter {

// THE TEMPLATE METHOD — final prevents subclasses from reordering steps
public final void export(String source, String destination) {
List<Record> records = loadData(source); // ← fixed step
List<String> formatted = transform(records); // ← abstract — varies
writeOutput(destination, formatted); // ← abstract — varies
cleanup(); // ← hook — optional override
System.out.println("Export complete: " + destination);
}

private List<Record> loadData(String source) {
System.out.println("Loading data from: " + source);
return recordRepository.findAll(); // ← shared logic — no duplication
}

protected abstract List<String> transform(List<Record> records); // ← subclass fills in
protected abstract void writeOutput(String dest, List<String> data); // ← subclass fills in

// Hook method — default is no-op; subclass may override
protected void cleanup() {}
}

// ── ConcreteClass A — CSV export ─────────────────────────────────────
public class CsvExporter extends DataExporter {

@Override
protected List<String> transform(List<Record> records) {
return records.stream()
.map(r -> r.id() + "," + r.name() + "," + r.value()) // ← CSV format
.collect(Collectors.toList());
}

@Override
protected void writeOutput(String dest, List<String> data) {
String csv = String.join("\n", data);
Files.writeString(Path.of(dest), csv);
}

@Override
protected void cleanup() {
System.out.println("Cleaning up temp CSV files"); // ← optional hook
}
}

// ── ConcreteClass B — JSON export ────────────────────────────────────
public class JsonExporter extends DataExporter {

private final ObjectMapper mapper = new ObjectMapper();

@Override
protected List<String> transform(List<Record> records) {
return records.stream()
.map(r -> { try { return mapper.writeValueAsString(r); }
catch (Exception e) { throw new RuntimeException(e); } })
.collect(Collectors.toList());
}

@Override
protected void writeOutput(String dest, List<String> data) {
String json = "[" + String.join(",", data) + "]";
Files.writeString(Path.of(dest), json);
}
// ← no cleanup override — uses default no-op hook
}

// ── Usage ──────────────────────────────────────────────────────────────
DataExporter exporter = new CsvExporter();
exporter.export("db://orders", "/tmp/orders.csv");

exporter = new JsonExporter();
exporter.export("db://orders", "/tmp/orders.json");

Spring's JdbcTemplate — Template Method in Production​

JdbcTemplate is the most famous Java Template Method: the algorithm skeleton (get connection → prepare statement → execute → map results → close resources) is fixed. You provide only the varying parts via callbacks:

@Autowired JdbcTemplate jdbc;

// You provide the mapping step (RowMapper) — JdbcTemplate handles the rest
List<User> users = jdbc.query(
"SELECT id, name, email FROM users WHERE active = ?",
(rs, rowNum) -> new User(rs.getLong("id"), rs.getString("name"), rs.getString("email")), // ← your step
true
);

JdbcTemplate owns all infrastructure steps (green). You provide one step (orange) — the row mapping. This is Template Method via callback instead of inheritance.

Spring Security's AbstractAuthenticationProcessingFilter​

Spring Security's authentication filter is a Template Method: the doFilterInternal() skeleton is fixed in AbstractAuthenticationProcessingFilter, and attemptAuthentication() is the abstract step you override:

public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

@Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) {
// ← your implementation: extract JWT, validate, build Authentication object
String token = extractBearerToken(req);
return getAuthenticationManager().authenticate(new JwtAuthToken(token));
}
// The rest of the filter lifecycle is handled by the superclass template
}

Trade-offs & When To Use / Avoid​

ProsCons
Template MethodEliminates code duplication across variants; algorithm structure is centralized; easy to extendUses inheritance — tightly couples subclass to base class; Java doesn't support multiple inheritance
vs StrategyLess indirection; natural for fixed pipelinesStrategy uses composition — more flexible; can swap at runtime; doesn't require inheritance
vs directly implementing full algorithmShared steps are written onceSubclasses can accidentally break the algorithm if templateMethod() is not final

When to use:

  • Multiple subclasses share the same algorithm skeleton but differ only in specific steps.
  • Framework hooks (Spring Security filters, Spring Batch listeners, JUnit lifecycle methods).
  • When the algorithm order is fixed but certain steps are customizable.

When to avoid:

  • When you want runtime algorithm swapping (use Strategy instead).
  • When inheritance depth becomes excessive — prefer composition.

Common Pitfalls​

  • Not marking templateMethod() as final — subclasses can override the template itself, reordering or skipping critical steps. Always make the template method final to protect the skeleton.
  • Too many abstract steps — if every step is abstract, the base class provides no value. Keep fixed steps in the base class and limit abstract steps to what truly varies.
  • Calling abstract hooks from the constructor — if the constructor calls an overridable method, the subclass override may run before the subclass is fully initialized. Avoid calling abstract or overridable methods in constructors.
  • Inheritance fragility — the "fragile base class" problem: changing a fixed step in the base class can unexpectedly break subclass behavior. Document your base class's contract clearly.

Interview Questions​

Beginner​

Q: What is the Template Method pattern? A: It defines an algorithm's skeleton in a base class method, with certain steps declared abstract or overridable. Subclasses implement the varying steps without changing the overall algorithm structure.

Q: Where does Java or Spring use Template Method? A: JdbcTemplate, RestTemplate, KafkaTemplate — Spring's *Template classes all fix the infrastructure skeleton and let you provide the row mapper/callback. Spring Security's AbstractAuthenticationProcessingFilter is another example. JUnit's setUp()/tearDown() hooks are classic Template Method hook methods.

Intermediate​

Q: What is the difference between Template Method and Strategy? A: Template Method uses inheritance — the skeleton is in a superclass and subclasses override steps. Strategy uses composition — the algorithm is a separate injected object. Template Method is compile-time fixed; Strategy can be swapped at runtime. Template Method is preferred for natural inheritance hierarchies with a fixed pipeline (like Spring filter chains); Strategy is preferred when algorithms should be interchangeable at runtime.

Q: Why should the template method be marked final? A: To prevent subclasses from overriding the algorithm skeleton itself — which could reorder, skip, or add steps in ways that break the contract. Subclasses should only override the designated abstract or hook steps, never the template itself.

Advanced​

Q: How does JdbcTemplate implement Template Method without inheritance? A: Instead of forcing you to subclass JdbcTemplate, it accepts a RowMapper or PreparedStatementSetter callback. The template calls your callback at the appropriate step. This is Template Method via functional interfaces / callback rather than inheritance — achieves the same inversion of control without a class hierarchy. This is sometimes called the Hollywood Principle ("don't call us, we'll call you").

Further Reading​

  • Strategy Pattern — Strategy is the composition-based alternative to Template Method for varying algorithms; essential for understanding their contrast.
  • Factory Method Pattern — Factory Method is structurally a Template Method where the varying step is object creation.
  • Decorator Pattern — Decorator wraps a component to add behavior; Template Method builds the behavior into the inheritance hierarchy.