Menu
Coddy logo textTech

Template Method Pattern

Part of the Object Oriented Programming section of Coddy's C# journey — lesson 59 of 70.

The Template Method pattern is a behavioral pattern that defines the skeleton of an algorithm in a base class, letting subclasses override specific steps without changing the overall structure. The base class controls the workflow, while derived classes customize individual pieces.

This pattern uses inheritance and abstract methods. The base class contains a "template method" that calls a sequence of steps, some of which are abstract and must be implemented by subclasses:

public abstract class DataProcessor
{
    // Template method - defines the algorithm structure
    public void Process()
    {
        ReadData();
        ProcessData();
        SaveData();
    }
    
    protected abstract void ReadData();
    protected abstract void ProcessData();
    protected virtual void SaveData() => Console.WriteLine("Saving to default location");
}

public class CsvProcessor : DataProcessor
{
    protected override void ReadData() => Console.WriteLine("Reading CSV file");
    protected override void ProcessData() => Console.WriteLine("Parsing CSV data");
}

public class JsonProcessor : DataProcessor
{
    protected override void ReadData() => Console.WriteLine("Reading JSON file");
    protected override void ProcessData() => Console.WriteLine("Parsing JSON data");
    protected override void SaveData() => Console.WriteLine("Saving to cloud");
}

Each subclass provides its own implementation while the base class enforces the order of operations:

DataProcessor csv = new CsvProcessor();
csv.Process();
// Reading CSV file
// Parsing CSV data
// Saving to default location

DataProcessor json = new JsonProcessor();
json.Process();
// Reading JSON file
// Parsing JSON data
// Saving to cloud

The Template Method pattern is ideal when multiple classes share the same algorithm structure but differ in specific steps. It promotes code reuse by placing common logic in the base class while allowing flexibility through overridable methods.

challenge icon

Challenge

Easy

Let's build a report generation system using the Template Method pattern. Different types of reports (like sales reports and inventory reports) share the same overall structure - they all need to gather data, format it, and output the result - but each report type handles these steps differently.

You'll organize your code across three files:

  • ReportGenerator.cs: Create an abstract ReportGenerator class in the Reporting namespace. This base class defines the skeleton of the report generation algorithm through a template method called Generate(). The method should call three steps in order: GatherData(), FormatReport(), and OutputReport(). Make GatherData() and FormatReport() abstract (each report type will implement these differently), while OutputReport() should be a virtual method with a default implementation that prints Sending to default printer.
  • ConcreteReports.cs: Create two concrete report classes in the same namespace that inherit from ReportGenerator:
    • SalesReport - implements GatherData() to print Collecting sales data from database and FormatReport() to print Formatting as sales summary
    • InventoryReport - implements GatherData() to print Scanning warehouse inventory, FormatReport() to print Formatting as inventory list, and overrides OutputReport() to print Sending to warehouse manager
  • Program.cs: Bring everything together by creating report instances and generating them. The template method ensures each report follows the same three-step process, but the specific behavior of each step varies based on the report type.

You will receive one input:

  • The report type: sales or inventory

Create the appropriate report generator based on the input and call its Generate() method.

For example, if the input is sales, the output should be:

Collecting sales data from database
Formatting as sales summary
Sending to default printer

If the input is inventory, the output should be:

Scanning warehouse inventory
Formatting as inventory list
Sending to warehouse manager

Notice how both reports follow the exact same algorithm structure (gather, format, output), but each step's implementation is customized. The SalesReport uses the default output behavior, while InventoryReport overrides it - demonstrating how the Template Method pattern lets you control which steps are mandatory to override and which can optionally be customized!

Cheat sheet

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

The base class contains a "template method" that calls a sequence of steps. Some steps are abstract (must be implemented by subclasses), while others can be virtual (optional to override):

public abstract class DataProcessor
{
    // Template method - defines the algorithm structure
    public void Process()
    {
        ReadData();
        ProcessData();
        SaveData();
    }
    
    protected abstract void ReadData();
    protected abstract void ProcessData();
    protected virtual void SaveData() => Console.WriteLine("Saving to default location");
}

Subclasses provide their own implementations while the base class enforces the order of operations:

public class CsvProcessor : DataProcessor
{
    protected override void ReadData() => Console.WriteLine("Reading CSV file");
    protected override void ProcessData() => Console.WriteLine("Parsing CSV data");
}

public class JsonProcessor : DataProcessor
{
    protected override void ReadData() => Console.WriteLine("Reading JSON file");
    protected override void ProcessData() => Console.WriteLine("Parsing JSON data");
    protected override void SaveData() => Console.WriteLine("Saving to cloud");
}

Usage:

DataProcessor csv = new CsvProcessor();
csv.Process();
// Reading CSV file
// Parsing CSV data
// Saving to default location

DataProcessor json = new JsonProcessor();
json.Process();
// Reading JSON file
// Parsing JSON data
// Saving to cloud

The Template Method pattern is ideal when multiple classes share the same algorithm structure but differ in specific steps. It promotes code reuse by placing common logic in the base class while allowing flexibility through overridable methods.

Try it yourself

using System;
using Reporting;

class Program
{
    public static void Main(string[] args)
    {
        string reportType = Console.ReadLine();
        
        // TODO: Create the appropriate report generator based on reportType
        // If reportType is "sales", create a SalesReport
        // If reportType is "inventory", create an InventoryReport
        // Then call the Generate() method on the report
        
    }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming