Template Method Pattern in C# — A Practical Guide
A behavioral design pattern that defines the structure of an algorithm in a base class while allowing subclasses to override specific steps for flexible customization.
In software development, certain algorithms follow a fixed structure but allow specific steps to vary. This is where the Template Method Pattern comes in. It lets you define the blueprint of an algorithm in a base class and delegate the variable parts to derived classes. This promotes code reuse, consistency, and flexibility—all while adhering to solid design principles.
🧠 What Is the Template Method Pattern?
The Template Method Pattern defines the skeleton of an algorithm in a base class but allows derived classes to modify or extend specific steps. The structure of the overall process remains intact, ensuring uniformity across implementations.
Definition (GoF):
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.
This approach enforces consistency in workflows while still supporting customization.
🛠️ How It Works in C#
Here’s a simple implementation in C#:
abstract class ReportGenerator
{
// Template method
public void GenerateReport()
{
ConnectToDb();
FetchData();
FormatData();
Export();
}
protected abstract void FetchData();
protected abstract void FormatData();
private void ConnectToDb()
{
Console.WriteLine("Connecting to DB...");
}
private void Export()
{
Console.WriteLine("Exporting report...");
}
}
class SalesReport : ReportGenerator
{
protected override void FetchData()
{
Console.WriteLine("Fetching sales data...");
}
protected override void FormatData()
{
Console.WriteLine("Formatting sales report...");
}
}
class InventoryReport : ReportGenerator
{
protected override void FetchData()
{
Console.WriteLine("Fetching inventory data...");
}
protected override void FormatData()
{
Console.WriteLine("Formatting inventory report...");
}
}
// Usage
var salesReport = new SalesReport();
salesReport.GenerateReport();
// Usage
InventoryReport inventoryReport = new InventoryReport();
inventoryReport.GenerateReport();
🧠 Key Point: The algorithm (report generation) is fixed, but the steps (fetching/formatting) are overridden by subclasses.
✅ Benefits and Alignment with SOLID Principles
Liskov Substitution Principle (LSP):
Derived classes can stand in for their base class without breaking the program. You can substituteCsvDataProcessor
forDataProcessor
and everything still works.Open/Closed Principle:
You can extend functionality via new subclasses without modifying existing code.
📌 When to Use Template Method
Consider this pattern in the following scenarios:
🔹 Standardizing Processes:
Ensure a fixed sequence of steps but allow customization at certain points.
🔹 Avoiding Code Duplication:
Place shared steps in a base class and only override the differing parts in subclasses.
🔹 Framework Design:
Use it when creating frameworks where some operations must remain unchanged, but others should be user-definable.
🔹 Best Practices Enforcement:
Force a consistent execution flow while allowing flexibility in implementation.
🌍 Real-World Use Cases
Web Frameworks:
HTTP request pipelines often follow a common structure (authentication → routing → rendering) but allow extensibility at each stage.Reporting Systems:
Report generation often follows the same process: fetch data → format → export, while format and export mechanisms vary.Data Processing Pipelines:
Common stages like extract → transform → load (ETL) remain fixed, but transformation logic changes by data type.
⚖️ Pros and Cons
✅ Strengths:
Code Reusability:
Centralizes common logic and reduces duplication.Behavioral Consistency:
Guarantees uniform workflows across implementations.Maintainability:
Workflow changes are made in one place—the base class.
❌ Weaknesses:
Tight Coupling:
Subclasses rely heavily on the base class structure.Inheritance Dependency:
Only applicable where inheritance is feasible—composition offers more flexibility.Lack of Runtime Flexibility:
Steps are fixed at compile time. Strategy Pattern allows dynamic behavior changes.
⚠️ Common Pitfalls
🚫 Overusing Inheritance:
Using Template Method where delegation or Strategy would be a better fit can lead to rigid, fragile codebases.
🚫 Breaking LSP:
Modifying the subclass behavior in ways that violate assumptions of the base class can cause bugs and unexpected behavior.
🧩 Final Thoughts
The Template Method Pattern is an excellent choice when you want to define a clear and consistent algorithm structure while allowing certain parts to vary. It's a staple in framework and library design, and when used correctly, it results in cleaner, more maintainable, and extensible code.
However, always consider the alternatives—sometimes composition or the Strategy Pattern offers more flexibility, especially when runtime adaptability is required.