June 17, 2026 in patterns5 minutes
A intermediate-level guide to Template Method: before-and-after java code and diagrams for a CS student.
Imagine you are building a data processing engine. You have different file formats—like plain text and HTML—that need to be processed. While the specific way you read an HTML tag is different from how you read a line of text, the high-level workflow remains identical: you open the file, you read the content, and you complete the process.
If you implement these as separate classes without any shared structure, you end up with “Duplicate Code.” This isn’t just annoying; it is a significant technical debt. If you decide to add a logging step or a security check to your workflow later, you have to remember to manually update every single class. Miss one, and your application behaves inconsistently.
public class TextDataProcessor {
// Duplicate workflow logic for text files
public void process(String filePath) {
System.out.println("Opening file: " + filePath);
System.out.println("Reading content from text...");
String content = "Hello, World!"; // Simulated read
System.out.println("Content: " + content);
}
}
public class HtmlDataProcessor {
// Duplicate workflow logic for HTML files
public void process(String filePath) {
System.out.println("Opening file: " + filePath);
System.out.println("Reading content from HTML...");
String content = "<h1>Hello</h1>"; // Simulated read
System.out.println("Content: " + content);
}
}Because TextDataProcessor and HtmlDataProcessor share no common ancestor, they are isolated islands. To a computer, their process methods look entirely unrelated, even though they follow the exact same sequence of events.
classDiagram
class TextDataProcessor {
+process(String filePath)
}
class HtmlDataProcessor {
+process(String filePath)
}
Think of a recipe for making different types of pancakes. The “template” or the core algorithm is fixed: first you mix ingredients, then you pour them on the pan, then you flip them, and finally you serve them.
You might change the type of flour (the implementation) or add blueberries (a specific step), but the sequence remains rigid. You wouldn’t suddenly decide to serve the pancakes before mixing the batter. The Template Method pattern uses this exact logic: it defines the skeleton of an algorithm in a base class, letting subclasses redefine certain steps without changing the algorithm’s structure.
To implement this, we use three specific Java concepts:
In this pattern, we create a final method in the superclass. The keyword final ensures that no subclass can override this method, effectively locking the workflow sequence so it cannot be accidentally broken. We then define “hook” methods—marked as protected abstract—which allow subclasses to plug in their specific logic.
sequenceDiagram
participant Subtype
Subtype ->> Subtype: process(filePath)
Note right of Subtype: The Template Method starts here
Subtype ->> Subtype: openFile(filePath)
Subtype ->> Subtype: readData()
Note over Subtype: Polymorphic dispatch to subclass logic
Subtype ->> Subtype: System.out.println("Processing complete.")
By refactoring the duplicate logic into a single base class, we centralize the workflow. The superclass DataProcessor now owns the “how” of the sequence, while the subclasses only care about the “what” of the specific data reading.
public abstract class DataProcessor {
// The Template Method defining the fixed workflow
public final void process(String filePath) {
openFile(filePath);
readData();
System.out.println("Processing complete.");
}
private void openFile(String filePath) {
System.out.println("Opening file: " + filePath);
}
// Abstract steps to be implemented by subclasses
protected abstract void readData();
}
public class TextDataProcessor extends DataProcessor {
@Override
protected void readData() {
System.out.println("Reading content from text...");
String content = "Hello, World!";
System.out.println("Content: " + content);
}
}
public class HtmlDataProcessor extends DataProcessor {
@Override
protected void readData() {
System.out.println("Reading content from HTML...");
String content = "<h1>Hello</h1>";
System.out.println("Content: " + content);
}
}Now, when you want to add a new file type—say, JSON—you simply create a new subclass and implement the readData method. You don’t have to worry about the opening or closing logic; that is already safely handled by the template.
classDiagram
class DataProcessor {
<<abstract>>
+final process(String filePath)
-openFile(String filePath)
#readData()*
}
class TextDataProcessor {
#readData()
}
class HtmlDataProcessor {
#readData()
}
DataProcessor <|-- TextDataProcessor
DataProcessor <|-- HtmlDataProcessor
The Template Method is a powerful behavioral pattern, but it is not a silver bullet.
Use it when:
final).Avoid it when:
final method within an abstract superclass to prevent logic drift.Executing the template method via a subclass
DataProcessor textProc = new TextDataProcessor();
textProc.process("data.txt");