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 cloudThe 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
EasyLet'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 abstractReportGeneratorclass in theReportingnamespace. This base class defines the skeleton of the report generation algorithm through a template method calledGenerate(). The method should call three steps in order:GatherData(),FormatReport(), andOutputReport(). MakeGatherData()andFormatReport()abstract (each report type will implement these differently), whileOutputReport()should be a virtual method with a default implementation that printsSending to default printer.ConcreteReports.cs: Create two concrete report classes in the same namespace that inherit fromReportGenerator:SalesReport- implementsGatherData()to printCollecting sales data from databaseandFormatReport()to printFormatting as sales summaryInventoryReport- implementsGatherData()to printScanning warehouse inventory,FormatReport()to printFormatting as inventory list, and overridesOutputReport()to printSending 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:
salesorinventory
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 printerIf the input is inventory, the output should be:
Scanning warehouse inventory
Formatting as inventory list
Sending to warehouse managerNotice 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 cloudThe 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
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesNamespaces & DirectivesIntro to Classes & ObjectsThe 'this' KeywordMethods and ParametersFields vs PropertiesConstructorsObject InitializersRecap - Simple Calculator4Inheritance
Basic Inheritance (:) SyntaxThe 'base' KeywordVirtual & Override KeywordsSealed ClassesThe 'object' Base ClassRecap - Employee Hierarchy7Advanced Features
Operator OverloadingIndexers (this[])ToString() OverrideExtension MethodsRecap - Custom List2Properties & Static Members
Auto-Implemented PropertiesRead/Write-Only PropertiesStatic Fields & MethodsStatic ClassesExpression-Bodied Members5Polymorphism & Interfaces
Compile vs Runtime PolyInterface vs Abstract ClassMultiple InterfacesExplicit InterfacesUpcasting & DowncastingRecap - Shape Calculator8Advanced OOP Concepts
Composition over InheritanceGenerics (Classes & Methods)Delegates and EventsAttributes and ReflectionIDisposable & using StatementDependency Injection Basics11Design Patterns Part 2
Command PatternAdapter PatternDecorator PatternTemplate Method PatternState PatternComposite Pattern