patron-template-method

Template Method Pattern: Defining the Skeleton

  • 5 min

The Template Method is a method that defines the skeleton of an algorithm and delegates some steps to subclasses. It preserves the overall order while allowing concrete details to vary.

Imagine we are making a program to prepare hot beverages. To make Tea, the algorithm is:

  1. Boil water.
  2. Put the tea bag in.
  3. Pour into cup.
  4. Add lemon.

To make Coffee, the algorithm is:

  1. Boil water.
  2. Filter the ground coffee.
  3. Pour into cup.
  4. Add sugar and milk.

If you notice, the skeleton is the same: Boil -> Brew -> Serve -> Season. Only steps 2 and 4 change.

If we copy and paste the code, we will have duplication. If tomorrow we change how we boil water, we’ll have to fix it in two places.

The Template Method pattern allows us to define the structure of an algorithm in a base class and delegate the implementation of specific steps to subclasses.

The Concept: The Hollywood Principle

This pattern is based on the Hollywood Principle: “Don’t call us, we’ll call you”.

The base class (the parent) is in control. It has the main method (the template) that calls the methods of the subclasses (the children) at the exact moment. The children do not control the flow; they only fill in the blanks left by the parent.

Implementation in C#

Let’s implement a Data Processing system. Imagine an ETL tool that reads data, processes it, and generates a report. We want to support CSV and PDF.

The Abstract Class (The Template)

This class defines the flow. Notice the GenerateReport method. That is the template method.

public abstract class ReportGenerator
{
    // THE TEMPLATE METHOD
    // Defines the skeleton of the algorithm.
    // Usually NOT virtual, to prevent children from breaking the flow.
    public void GenerateReport()
    {
        Console.WriteLine("\n--- Starting Report Generation ---");
        
        ConnectToDatabase(); // Common step
        
        var data = ExtractData(); // Abstract step (children decide how)
        
        var processedData = ProcessData(data); // Common step (or hook)
        
        Export(processedData); // Abstract step
        
        CloseDatabase(); // Common step
        
        Console.WriteLine("--- Report Finished ---");
    }

    // --- Common Steps (Already implemented) ---
    protected void ConnectToDatabase()
    {
        Console.WriteLine("1. Connecting to common database...");
    }

    protected void CloseDatabase()
    {
        Console.WriteLine("5. Closing database connection.");
    }

    // --- Default Steps (Hooks) ---
    // Children CAN override it if they want, but they are not forced to.
    protected virtual string ProcessData(string data)
    {
        Console.WriteLine("3. Processing data (base logic)...");
        return data.ToUpper(); // By default, we put it in uppercase
    }

    // --- Abstract Steps (Mandatory) ---
    // Children MUST implement them.
    protected abstract string ExtractData();
    protected abstract void Export(string data);
}
Copied!

The Concrete Implementations

Now let’s create the “Flavors” of our algorithm.

CSV Report:

public class CSVReport : ReportGenerator
{
    protected override string ExtractData()
    {
        Console.WriteLine("2. Extracting data for CSV (fast)...");
        return "data,csv,example";
    }

    protected override void Export(string data)
    {
        Console.WriteLine($"4. Saving .csv file with content: [{data}]");
    }
}
Copied!

PDF Report:

public class PDFReport : ReportGenerator
{
    protected override string ExtractData()
    {
        Console.WriteLine("2. Extracting data for PDF (includes images)...");
        return "<pdf>visual data</pdf>";
    }

    // Here we choose to override the Hook as well
    protected override string ProcessData(string data)
    {
        Console.WriteLine("3. Processing data for PDF (adding styles)...");
        return $"** {data} **";
    }

    protected override void Export(string data)
    {
        Console.WriteLine($"4. Rendering .pdf document with content: {data}");
    }
}
Copied!

The Client

The client does not care about the internal flow; it only calls the public method.

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(">>> Client requests CSV report:");
        var csv = new CSVReport();
        csv.GenerateReport(); // The parent orchestrates, the child works

        Console.WriteLine("\n>>> Client requests PDF report:");
        var pdf = new PDFReport();
        pdf.GenerateReport();
    }
}
Copied!

Output:

>>> Client requests CSV report:

--- Starting Report Generation ---
1. Connecting to common database...
2. Extracting data for CSV (fast)...
3. Processing data (base logic)...
4. Saving .csv file with content: [DATA,CSV,EXAMPLE]
5. Closing database connection.
--- Report Finished ---

>>> Client requests PDF report:

--- Starting Report Generation ---
1. Connecting to common database...
2. Extracting data for PDF (includes images)...
3. Processing data for PDF (adding styles)...
4. Rendering .pdf document with content: ** <pdf>visual data</pdf> **
5. Closing database connection.
--- Report Finished ---
Copied!

Hooks

A key concept in this pattern are Hooks. They are methods in the base class that:

  1. Are empty or have a minimal default implementation.
  2. Are not abstract.

They provide optional extension points.

  • Example: virtual void OnReportGenerated() { }
  • The base class calls it at the end. If the subclass wants to log or send an email, it overrides the method. If not, nothing happens.

Template Method vs Strategy

This is the eternal question, because both “customize algorithms”.

FeatureTemplate MethodStrategy
MechanismUses Inheritance (Subclasses).Uses Composition (Interfaces).
LevelModifies parts of an algorithm.Replaces the entire algorithm.
RelationshipStatic (defined at compile time).Dynamic (swappable at runtime).
  • Use Template Method when you have a fixed algorithm and only want to vary some specific steps.
  • Use Strategy when you have several completely different algorithms to do the same thing and you want to change them on the fly.

The Template Method appears frequently in frameworks. When you override lifecycle methods, the framework maintains the general flow and your code customizes specific extension points.