AlgoMaster Logo

Template Method Design Pattern

Ashish

Ashish Pratap Singh

5 min read

The Template Method Design Pattern is a behavioral design pattern that defines the skeleton of an algorithm in a base class, but allows subclasses to override specific steps of the algorithm without changing its overall structure.

It’s particularly useful in situations where:

  • You have a well-defined sequence of steps to perform a task.
  • Some parts of the process are shared across all implementations.
  • You want to allow subclasses to customize specific steps without rewriting the whole algorithm.

When you're solving a problem that follows a common high-level structure but has slight variations in a few steps, it's tempting to write a single method with branching logic like if-else or switch statements to handle those differences.

For example, a DataExporter might use if conditions to export in CSV, JSON, or XML format.

But as new variations are added, the method becomes bloated, hard to follow, and violates the Single Responsibility Principle and the Open/Closed Principle.

The Template Method Pattern solves this by capturing the common workflow in a base class and pushing the customizable steps into subclasses, ensuring that the overall structure remains consistent while allowing flexibility where needed.

Let’s walk through a real-world example and see how we can apply the Template Method Pattern to build flexible, extensible, and reusable workflows.

1. The Problem: Exporting Reports

Let’s say you’re building a tool that allows your application to export reports in different formats — such as CSVPDF, and Excel.

On the surface, each report exporter has a different output format, but underneath, the overall process is almost identical.

Here’s the high-level workflow followed by each exporter:

  1. Prepare Data: Gather and organize the data to be exported.
  2. Open File: Create or open the output file in the desired format.
  3. Write Header: Output column headers or metadata (format-specific).
  4. Write Data Rows: Iterate through the dataset and write the rows (format-specific).
  5. Write Footer: Add optional summary or footer info.
  6. Close File: Finalize and close the output file.

Despite these similarities, if you implement each exporter naively, you'll likely duplicate a lot of logic, and that comes at a cost.

ReportData

CsvReportExporterNaive

PdfReportExporterNaive

Client Code

What’s Wrong with This Design?

While this approach might seem straightforward at first, it comes with several serious design drawbacks:

Code Duplication

The same steps — preparing data, opening/closing files, writing footers — are repeated in every exporter class. Add an ExcelReportExporterNaive, and you're copying the same boilerplate for the third time.

Maintenance Overhead

If you decide to change how data is prepared or how files are closed (e.g., to add logging or error handling), you’ll have to update every exporter class manually — increasing the risk of introducing bugs.

Inconsistent Behavior

Since each exporter implements the entire workflow on its own, there’s a real danger that a developer might:

  • Omit a step (e.g., skip the footer),
  • Change the order (write data before the header), or
  • Handle a failure differently in one exporter but not another.

This leads to inconsistent logic and fragile code.

Poor Extensibility

Adding a new report format means copying a full class, pasting boilerplate, and modifying only a few lines — violating the DRY (Don't Repeat Yourself) principle.

What We Really Need

We need a way to:

  • Define the common report export workflow once, in a base class.
  • Allow subclasses to override only the format-specific steps, like writing headers and data rows.
  • Ensure that all report exporters follow the same sequence, and enforce consistency in the algorithm structure.

This is exactly what the Template Method Pattern is designed for.

2. What is the Template Method Pattern

The Template Method pattern defines the skeleton of an algorithm in a method, deferring some steps to subclasses. It allows you to keep the overall structure of the process consistent, while giving subclasses the flexibility to customize specific parts of the algorithm.

Class Diagram

1. AbstractClass (e.g., AbstractReportExporter):

  • Contains the template method: a final method (e.g., exportReport()) that defines the fixed sequence of steps for the algorithm.
  • Declares one or more abstract methods (also known as primitive operations) that must be implemented by subclasses. These represent steps that may vary across different implementations.
  • May include hook methods: concrete methods with default behavior that subclasses can optionally override to customize certain points in the algorithm.

2. Concrete Classes (e.g., CsvReportExporterPdfReportExporter)

  • Extend the abstract base class.
  • Provide implementations for the abstract methods defined in the template.
  • Optionally override hook methods to customize optional behavior like adding footers or formatting styles.

3. Implementing Template Method

Let’s refactor our report exporting system using the Template Method Pattern.

The goal is to extract the common report generation workflow into a single base class, while allowing each format-specific exporter to customize only the parts that differ.

1. Create the Abstract Base Class (AbstractReportExporter)

This class contains the template method, which defines the fixed structure of the report export process. It also defines abstract methods (to be implemented by subclasses) and hook methods (which subclasses can override if needed).

2. Implement Concrete Exporters

Each concrete class will extend AbstractReportExporter and implement the format-specific steps.

CSV Exporter

Pdf Exporter

Client Code Uses the Abstract Class

Now the client code doesn’t need to worry about the internal steps of report generation. It simply creates the appropriate exporter and calls the exportReport() method.

Using Template Method pattern, we have:

  • Eliminated code duplication by extracting the shared export process into a base class.
  • Ensured consistency across all exporters by enforcing the same algorithm structure.
  • Made the system extensible — adding a new exporter (e.g., Excel) only requires creating a new subclass.
  • Improved maintainability — common logic changes only in one place.
  • Reduced risk of errors — the order of steps is controlled and protected by the abstract base class.