The Template Method Pattern
The Template Method Pattern
The Template Method Pattern defines the skeleton of an algorithm in a base class but lets subclasses override specific steps without changing the overall algorithm structure. The base class controls the flow, and subclasses plug in their own implementations for individual steps. This is a powerful way to enforce consistency while allowing flexibility.
Why Template Method Matters
Consider a data pipeline. Every pipeline follows the same general flow: read raw data, parse it into objects, validate the data, and build a final output. The order never changes, but the parsing logic differs between CSV, JSON, and XML sources. Template Method keeps the pipeline sequence in the base class while subclasses provide the parsing step.
Class Diagram
+------------------------+
| DataPipeline |
+------------------------+
| + execute() |
| final { |
| readData(); |
| parseData(); |
| validate(); |
| buildOutput(); |
| } |
+-----+------------------+
|
| subclasses override steps
|
+------+------+---------------+
| | |
+--+--------+ +--+----------+ +-+-----------+
| CsvPipeline| |JsonPipeline| | XmlPipeline |
+------------+ +-------------+ +-------------+
| parseData()| | parseData() | | parseData() |
+------------+ +-------------+ +-------------+
Code Example
The base class defines the template method. Notice the keyword final on the template method itself - subclasses cannot change the algorithm sequence:
public abstract class DataPipeline {
public final void execute() {
String raw = readData();
List<String> records = parseData(raw);
validate(records);
buildOutput(records);
}
protected abstract String readData();
protected abstract List<String> parseData(String raw);
protected void validate(List<String> records) {
if (records.isEmpty()) {
throw new RuntimeException("No records found");
}
}
protected abstract void buildOutput(List<String> records);
}
Subclasses fill in the steps. The CSV pipeline reads from a file and splits lines:
public class CsvPipeline extends DataPipeline {
protected String readData() {
return "Alice,100
Bob,200";
}
protected List<String> parseData(String raw) {
return Arrays.asList(raw.split("\n"));
}
protected void buildOutput(List<String> records) {
for (String row : records) {
System.out.println("Processing: " + row);
}
}
}
// Usage - client only calls execute()
DataPipeline pipeline = new CsvPipeline();
pipeline.execute();
Hook Methods
You can add optional steps by providing default empty implementations in the base class - called hook methods. Subclasses may override them or leave them empty. This lets the base class call hooks before or after critical steps without forcing subclasses to implement every single one.
Real-World Use
Java servlets use this pattern. The HttpServlet class defines service() as the template method. It reads the HTTP method and calls doGet(), doPost(), and so on. You override only the methods you need. Build frameworks, test runners, and game loops also rely on this approach.
Key Points
- The base class controls the algorithm skeleton with a final template method.
- Subclasses override individual steps without changing the overall flow.
- Default hook methods provide optional extension points.
- Enforces consistency across multiple implementations.
- Can lead to a deep inheritance hierarchy - consider composition alternatives for complex cases.