Skip to Content
Course content

156: The Template Method Pattern

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've noticed a recurring trend when developers first encounter the Template Method pattern: they mistake it for the Strategy pattern. On the surface, they look identical because both involve an abstract base or interface and a few concrete implementations. You might think, "I'm just using polymorphism to swap out logic; why does this need a special name?"

"It's just the Strategy Pattern with a different name"

If you treat the Template Method like the Strategy pattern, you end up duplicating the "boring" parts of your code. Let's say you're building a system to export data to different formats—CSV and PDF. In a Strategy-like approach, you'd create an Exporter interface with a single export() method. Each implementation would then handle opening the file, writing the header, writing the data, and closing the file.

// The "Strategy" approach (Wrong for this specific need)
public interface Exporter {
    void export(Data data);
}

public class CsvExporter implements Exporter {
    public void export(Data data) {
        System.out.println("Opening CSV file...");
        System.out.println("Writing CSV Header...");
        System.out.println("Writing CSV Data...");
        System.out.println("Closing CSV file...");
    }
}

public class PdfExporter implements Exporter {
    public void export(Data data) {
        System.out.println("Opening PDF file...");
        System.out.println("Writing PDF Header...");
        System.out.println("Writing PDF Data...");
        System.out.println("Closing PDF file...");
    }
}

See the problem? The sequence of events—Open, Header, Data, Close—is identical in both classes. If you decide tomorrow that you need to add a "Log export start time" step, you have to hunt down every single implementation and manually insert that line. That's a maintenance nightmare waiting to happen.

Enforcing the Algorithm's Skeleton

The Template Method pattern isn't about swapping the entire algorithm; it's about fixing the skeleton of the algorithm in a base class and letting subclasses fill in the specific blanks. The base class controls the workflow, and the subclasses provide the implementation for the individual steps.

Here is how I would actually structure that exporter. Notice how the export()` method is marked as final. This is crucial. I don't want a subclass "accidentally" forgetting to close the file or skipping the header.

public abstract class DataExporter {
    // This is the Template Method
    public final void export(Data data) {
        openFile();
        writeHeader();
        writeData(data);
        closeFile();
    }

    private void openFile() {
        System.out.println("Opening file stream...");
    }

    private void closeFile() {
        System.out.println("Closing file stream...");
    }

    // These are the "blanks" subclasses must fill in
    protected abstract void writeHeader();
    protected abstract void writeData(Data data);
}

public class CsvExporter extends DataExporter {
    @Override
    protected void writeHeader() {
        System.out.println("Writing CSV Header: ID, Name, Value");
    }

    @Override
    protected void writeData(Data data) {
        System.out.println("Writing data as comma-separated values.");
    }
}

By doing this, the DataExporter owns the "how" (the process), while the CsvExporter owns the "what" (the format). I've effectively removed the duplication and guaranteed that the file is always closed, regardless of the format.

Adding Flexibility with Hooks

Sometimes, a subclass needs to do something optional. I don't want to force every single exporter to implement a method if they don't need it. This is where "hooks" come in. A hook is simply a method in the base class that has a default (usually empty) implementation. Subclasses can override it if they want to, but they aren't required to.

For example, imagine some exports need to be compressed into a ZIP file, but others don't. I'd add a hook like this to the base class:

protected void compressFile() {
    // Default implementation: do nothing.
}

Then, I'd add compressFile() as a step inside the final export()` method. Most exporters will just ignore it, but my ZipPdfExporter can override it to add that specific functionality without breaking the overall template. It's a subtle but powerful way to keep your hierarchy clean.




📋 Practical Task

Build a Custom Log File Processor

You are tasked with creating a log processing system that reads files and performs different actions based on the log type (e.g., Security logs vs. Application logs). The workflow for all log processors must be: 1. Read the file. 2. Validate the log format. 3. Parse the entries. 4. Close the reader.

Your Requirements:

  • Create an abstract class LogProcessor.
  • Implement a final method called processLog() that defines the four-step sequence.
  • Implement read()` and close()` as private methods in the base class (since they are the same for all logs).
  • Define validateFormat()` and parseEntries()` as abstract methods.
  • Create two concrete classes: SecurityLogProcessor and AppLogProcessor that provide their own specific validation and parsing logic.
  • Add an optional "hook" method called alertAdmin()` in the base class that does nothing by default, but is called at the end of processLog(). Override this hook only in the SecurityLogProcessor to print "CRITICAL: Security breach detected!"
Rating
0 0

There are no comments for now.

to be the first to leave a comment.