DEV Community

Cover image for Design Patterns Simplified: Part 13 — Template Method Pattern (a.k.a. “The Recipe Pattern”)
Prateek Prabhakar
Prateek Prabhakar

Posted on • Edited on • Originally published at Medium

Design Patterns Simplified: Part 13 — Template Method Pattern (a.k.a. “The Recipe Pattern”)

The Template Method Pattern belongs to the Behavioral category of design patterns.

Why? Because it defines how an algorithm runs, step by step, while letting child classes tweak only the steps that vary.

Think of it like a cooking recipe. The steps are fixed,
Gather ingredients
Cook
Serve

But how you cook depends on the dish. You might make pasta or toss noodles, both follow the same structure(boil them first, put in the pan, etc.) but different steps.

The Template Method Pattern in a nutshell, is like the “recipe” your base class writes and subclasses just fill in the flavor.


What Is the Template Method Pattern?

The Template Method Pattern defines the skeleton of an algorithm in a base class, and lets subclasses override specific steps without changing the overall structure.

It helps,

  • Reuse shared logic
  • Keep algorithms consistent
  • Allow extensions via subclassing
  • Avoid code duplication

Report Generation System

Let’s say you are building an enterprise application that generates various types of reports like, Sales Report, Inventory Report, User Activity Report.

Every report follows the same high level process,

  1. Connect to the database
  2. Fetch relevant data
  3. Format the data
  4. Add a header and footer
  5. Export to PDF

But the data and formatting vary based on report type.
Instead of repeating the full process in every report class, we define it once in a base class, and only override what is different.
That is the power of the Template Method Pattern.

What does the code look like?

The basic setup would involve the below 3 components.

  1. Abstract class: defines the template (i.e. the fixed process)
  2. Concrete subclasses: override the specific steps
  3. Hook methods: optional steps that can be overridden if needed
//Step 1: Define the Template (Base Class)
Class ReportGenerator
    Method GenerateReport()
        ConnectToDatabase()    // Step 1: Common step
        FetchData()            // Step 2: Customized by subclass
        FormatData()           // Step 3: Customized by subclass
        AddHeader()            // Step 4: Common step
        AddFooter()            // Step 5: Common step
        Export()               // Step 6: Common step

    Method ConnectToDatabase()
        Print "Connecting to database..."

    // Variable steps
    Abstract Method FetchData()  // To be implemented by subclasses
    Abstract Method FormatData() // To be implemented by subclasses

    // Optional hooks
    Method AddHeader()
        Print "Adding standard header..."

    Method AddFooter()
        Print "Adding standard footer..."

    Method Export()
        Print "Exporting report to PDF..."

//Step 2: Define Concrete Reports
Class SalesReport extends ReportGenerator
    Method FetchData()
        Print "Fetching sales data for current month..."

    Method FormatData()
        Print "Creating bar chart for sales..."

Class InventoryReport extends ReportGenerator
    Method FetchData()
        Print "Fetching stock levels from warehouse DB..."

    Method FormatData()
        Print "Formatting inventory into table layout..."

//caller logic
report = new SalesReport()
report.GenerateReport()

// Output:
// Connecting to database...
// Fetching sales data for current month...
// Creating bar chart for sales...
// Adding standard header...
// Adding standard footer...
// Exporting report to PDF...

report = new InventoryReport()
report.GenerateReport()

// Output:
// Connecting to database...
// Fetching stock levels from warehouse DB...
// Formatting inventory into table layout...
// Adding standard header...
// Adding standard footer...
// Exporting report to PDF...

Enter fullscreen mode Exit fullscreen mode

ReportGenerator is an abstract class that defines how a report should be generated, but not the specific details for every report.

The method GenerateReport() is the template method. This method is not meant to be overridden. It controls the flow. It outlines the exact sequence of steps to generate a report.

Connect to the database
Fetch data
Format the data
Add headers and footers
Export the final report

Steps like ConnectToDatabase(), AddHeader(), AddFooter(), and Export() are shared across all reports, so they are implemented once in the base class. AddHeader() and AddFooter() are basically hook methods that are optional steps the subclasses can override if they want to customize these parts, otherwise, the base implementation runs.

But steps like FetchData() and FormatData() may vary for each report type (Sales report vs. Inventory report), so they are declared as abstract methods. Subclasses must provide their own implementation.
This way, the base class ensures consistent structure, while subclasses customize the variable parts.

What Did We Achieve?

  • Consistent structure: All reports follow the same, tested process.
  • Flexible overrides: Customize only what’s necessary without changing the flow.
  • No code duplication: Common logic lives at one place in the base class.
  • Clear control: The base class owns the workflow and subclasses plug in their specifics.

When Should You Use It?

  • You have an algorithm with a fixed sequence of steps but some steps vary
  • You are building a framework or base class where extensions should follow a strict flow
  • You want to enforce consistency across subclasses while allowing custom behavior

Use Cases?

  • Report/document generation workflows
  • Unit test lifecycle (setup → test → teardown)
  • Order processing pipelines
  • File import/export processing
  • Game engine loops (initialize → update → render)
  • Image processing workflows (load → process → save)

To summarize, it would be apt to say,

The Template Method Pattern lets you define a fixed recipe, and delegate specific steps to child classes.
“It is the design pattern equivalent of a chef preparing a meal. The steps are fixed, but the ingredients vary.”

It gives your code Reusability, Structure and Controlled flexibility.

Next up in the series: State Pattern. Let's dive in!


BONUS: Isn't This Similar to Strategy Pattern?

At first glance, Template Method and Strategy Patterns may seem alike. Both let you vary parts of an algorithm. But the difference lies in who controls the flow.

Let us understand it through a quick story.

You work at a company that generates different types of reports -Sales, Inventory, Feedback (as we took the example in the article above). Your application requirements are as below,

All reports getting generated via this application must follow the same process.

  1. Connect to DB
  2. Fetch Data
  3. Format Data
  4. Add Header & Footer
  5. Export to PDF

So, here the overall flow is fixed, but steps like data fetching and formatting differ per report type.
To implement this functionality, you create a base class ReportGenerator with a method GenerateReport() that defines this flow. Subclasses override the steps that vary.
That is Template Method Pattern.

What you are effectively saying is,

“This is how we have a fixed structure setup. You can tweak parts, but not the process.”

A week passes by and the product comes up with a new requirement,

“We want to export reports in different formats - PDF, Excel, or HTML depending on user preference.”

Now again, the flow stays the same, only the export format changes dynamically.

So you define a IReportExportStrategy interface and implement different export classes. You inject the appropriate strategy at runtime based on the configuration and send the report.
That is Strategy Pattern.

This time what you are saying is,

“The task stays the same. You choose the tool to do the final step.”

Differences:

Template Method vs Strategy Pattern

To summarize the difference between the two, you should,

Use Template Method when the flow is fixed and only steps vary.
Use Strategy Pattern when the task is fixed but the way to perform it varies, and should be chosen at runtime.

Top comments (0)