Showing posts with label Template Method Design Pattern. Show all posts
Showing posts with label Template Method Design Pattern. Show all posts

Thursday, July 30, 2026

Template Method Design Pattern

Mastering Design Patterns in C# and ASP.NET Core

Part 4.10 – Template Method Design Pattern

Series: Design Patterns in C# and ASP.NET Core
Pattern Category: Behavioral Design Pattern
Difficulty: ⭐⭐⭐☆☆ Intermediate
Prerequisites: C#, OOP, Inheritance, Abstract Classes, Interfaces, SOLID Principles, Dependency Injection


Table of Contents

  1. Introduction

  2. What is the Template Method Design Pattern?

  3. Why Do We Need the Template Method Pattern?

  4. The Problem with Duplicate Algorithms

  5. Real-World Analogy

  6. Template Method Pattern Structure

  7. UML Class Diagram

  8. Components of the Pattern

  9. Template Method vs Strategy

  10. Complete C# Console Application

  11. Data Processing Example

  12. Payment Processing Example

  13. Hook Methods

  14. ASP.NET Core Implementation

  15. Template Method with Dependency Injection

  16. Real-World Enterprise Examples

  17. Template Method vs Factory Method

  18. Template Method vs Strategy

  19. Template Method vs State

  20. Advantages

  21. Disadvantages

  22. Best Practices

  23. Common Mistakes

  24. Unit Testing

  25. Interview Questions

  26. When Should You Use Template Method?

  27. When Should You Avoid It?

  28. Key Takeaways

  29. Conclusion

  30. Coming Up Next


1. Introduction

In enterprise applications, we often have several processes that follow the same overall sequence, but individual steps differ.

For example, consider importing data from different sources:

Read Data
    ↓
Validate Data
    ↓
Transform Data
    ↓
Save Data
    ↓
Send Notification

The overall workflow is the same.

However, the implementation can differ for:

CSV
Excel
JSON
XML
Database
External API

If we implement the complete workflow separately for every data source, we can end up with duplicated code.

The Template Method Design Pattern provides a solution.

It allows a base class to define the skeleton of an algorithm, while derived classes implement or customize specific steps.


2. What is the Template Method Design Pattern?

The Template Method Design Pattern defines the skeleton of an algorithm in a base class and allows subclasses to redefine certain steps without changing the overall algorithm structure.

The important concept is:

The base class controls the overall workflow, while derived classes customize individual steps.

For example:

                DataProcessor
                     |
             ProcessData()
                     |
        +------------+------------+
        |            |            |
    ReadData()   Validate()   SaveData()
        ↑            ↑            ↑
        |            |            |
      CSV          CSV          CSV
      JSON         JSON         JSON
      XML          XML          XML

The sequence remains controlled by the base class.


3. Why Do We Need the Template Method Pattern?

Suppose we have three types of data processors:

CSVProcessor
JSONProcessor
XMLProcessor

All of them perform:

Read
Validate
Transform
Save

Without Template Method, we might write:

public void ProcessCsv()
{
    ReadCsv();
    ValidateCsv();
    TransformCsv();
    SaveCsv();
}

Then:

public void ProcessJson()
{
    ReadJson();
    ValidateJson();
    TransformJson();
    SaveJson();
}

And:

public void ProcessXml()
{
    ReadXml();
    ValidateXml();
    TransformXml();
    SaveXml();
}

The sequence is duplicated.

This creates problems.

Problem 1 – Code Duplication

The workflow is repeated.

Problem 2 – Inconsistent Processing

One implementation might accidentally change the order.

For example:

CSV:
Read → Validate → Transform → Save

JSON:
Read → Transform → Validate → Save

Problem 3 – Difficult Maintenance

Changing the workflow requires modifying multiple classes.

Problem 4 – Business Rules Can Drift

Different implementations may gradually behave differently.

The Template Method Pattern centralizes the algorithm.


4. The Problem with Duplicate Algorithms

Imagine a payment workflow:

1. Validate payment
2. Authenticate customer
3. Process payment
4. Save transaction
5. Send notification

Credit Card and Bank Transfer may have different implementations of individual steps.

But the overall process should remain:

Validate
   ↓
Authenticate
   ↓
Process
   ↓
Save
   ↓
Notify

The Template Method Pattern lets the base class enforce this sequence.


5. Real-World Analogy

Consider preparing different types of tea.

The overall recipe is:

Boil Water
    ↓
Prepare Ingredients
    ↓
Add Ingredients
    ↓
Pour into Cup
    ↓
Serve

For:

Green Tea
Black Tea
Herbal Tea

the sequence is similar, but some steps differ.

The template is:

PrepareTea()
    |
    +-- BoilWater()
    +-- AddIngredients()
    +-- Brew()
    +-- Serve()

Different tea types customize the appropriate steps.

This is exactly the idea behind Template Method.


6. Template Method Pattern Structure

The basic structure is:

                  AbstractClass
                       |
                 TemplateMethod()
                       |
       +---------------+---------------+
       |               |               |
       ↓               ↓               ↓
   StepOne()       StepTwo()       StepThree()
       ↑               ↑               ↑
       |               |               |
 ConcreteClassA   ConcreteClassB   ConcreteClassC

The base class owns the algorithm.

Derived classes own the variable parts.


7. UML Class Diagram

                    +--------------------------------+
                    |       AbstractProcessor        |
                    +--------------------------------+
                    |                                |
                    | + Process()                    |
                    |   <<Template Method>>           |
                    |                                |
                    | # ReadData()                    |
                    | # ValidateData()                |
                    | # TransformData()               |
                    | # SaveData()                    |
                    +---------------+----------------+
                                    |
                    +---------------+---------------+
                    |                               |
                    ↓                               ↓
          +--------------------+          +--------------------+
          | CsvProcessor       |          | JsonProcessor      |
          +--------------------+          +--------------------+
          | ReadData()         |          | ReadData()         |
          | ValidateData()     |          | ValidateData()     |
          | TransformData()    |          | TransformData()    |
          | SaveData()         |          | SaveData()         |
          +--------------------+          +--------------------+

The key relationship is:

AbstractClass
      ↑
      |
Concrete Classes

This is an inheritance-based pattern.


8. Components of the Pattern

There are typically three important components.

8.1 Abstract Class

Contains the overall algorithm.

public abstract class DataProcessor
{
    public void Process()
    {
        ReadData();
        ValidateData();
        TransformData();
        SaveData();
    }

    protected abstract void ReadData();

    protected abstract void ValidateData();

    protected abstract void TransformData();

    protected abstract void SaveData();
}

8.2 Template Method

The method:

public void Process()

is the Template Method.

It defines the sequence:

Read
 ↓
Validate
 ↓
Transform
 ↓
Save

8.3 Concrete Classes

Concrete classes implement individual steps.

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

    protected override void ValidateData()
    {
        Console.WriteLine("Validating CSV data...");
    }

    protected override void TransformData()
    {
        Console.WriteLine("Transforming CSV data...");
    }

    protected override void SaveData()
    {
        Console.WriteLine("Saving CSV data...");
    }
}

9. Why Should the Template Method Usually Be Non-Overridable?

A common implementation is:

public void Process()
{
    ReadData();
    ValidateData();
    TransformData();
    SaveData();
}

The method is intentionally not marked virtual.

Why?

Because the base class should control the algorithm's sequence.

If derived classes could override it, they could accidentally change:

Read → Validate → Transform → Save

into:

Read → Save → Validate → Transform

Therefore, the Template Method itself is commonly kept stable while individual steps remain customizable.


10. Complete C# Console Application

Let's create a complete example.

Base Class

public abstract class DataProcessor
{
    public void Process()
    {
        Console.WriteLine("Starting processing...");

        ReadData();
        ValidateData();
        TransformData();

        if (ShouldSave())
        {
            SaveData();
        }

        Notify();

        Console.WriteLine("Processing completed.");
    }

    protected abstract void ReadData();

    protected abstract void ValidateData();

    protected abstract void TransformData();

    protected abstract void SaveData();

    protected virtual bool ShouldSave()
    {
        return true;
    }

    protected virtual void Notify()
    {
        Console.WriteLine("Notification sent.");
    }
}

Notice something important:

protected virtual bool ShouldSave()

and:

protected virtual void Notify()

These are hook methods.

We'll discuss them shortly.


11. CSV Processor

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

    protected override void ValidateData()
    {
        Console.WriteLine("Validating CSV data...");
    }

    protected override void TransformData()
    {
        Console.WriteLine("Transforming CSV data...");
    }

    protected override void SaveData()
    {
        Console.WriteLine("Saving CSV data to database...");
    }
}

12. JSON Processor

public class JsonProcessor : DataProcessor
{
    protected override void ReadData()
    {
        Console.WriteLine("Reading JSON data...");
    }

    protected override void ValidateData()
    {
        Console.WriteLine("Validating JSON data...");
    }

    protected override void TransformData()
    {
        Console.WriteLine("Transforming JSON data...");
    }

    protected override void SaveData()
    {
        Console.WriteLine("Saving JSON data to database...");
    }
}

13. Program.cs

var csvProcessor = new CsvProcessor();

csvProcessor.Process();

Console.WriteLine();

var jsonProcessor = new JsonProcessor();

jsonProcessor.Process();

The output will follow the same overall structure:

Starting processing...
Reading CSV file...
Validating CSV data...
Transforming CSV data...
Saving CSV data to database...
Notification sent.
Processing completed.

Starting processing...
Reading JSON data...
Validating JSON data...
Transforming JSON data...
Saving JSON data to database...
Notification sent.
Processing completed.

Notice that the sequence is controlled by the base class.


14. Understanding the Flow

When we call:

csvProcessor.Process();

the method being executed is:

DataProcessor.Process()

The base class executes:

Process()
   |
   +-- ReadData()
   |
   +-- ValidateData()
   |
   +-- TransformData()
   |
   +-- ShouldSave()
   |
   +-- SaveData()
   |
   +-- Notify()

Because of polymorphism, the actual implementation of:

ReadData()
ValidateData()
TransformData()
SaveData()

comes from:

CsvProcessor

This is one of the most important concepts behind the Template Method Pattern.


15. Hook Methods

A hook method is an optional operation that allows subclasses to customize or control part of the algorithm.

For example:

protected virtual bool ShouldSave()
{
    return true;
}

A subclass can override it:

protected override bool ShouldSave()
{
    return false;
}

Now the Template Method:

if (ShouldSave())
{
    SaveData();
}

can conditionally skip saving.


16. Abstract Methods vs Hook Methods

This distinction is important.

Abstract Method

Must be implemented by derived classes.

protected abstract void ReadData();

Virtual Hook

Provides a default implementation.

protected virtual void Notify()
{
    Console.WriteLine("Notification sent.");
}

Derived classes can override it if required.

Summary

Abstract Method
    ↓
Mandatory customization

Hook Method
    ↓
Optional customization

17. Payment Processing Example

Now let's consider a more realistic enterprise scenario.

Suppose multiple payment types follow this workflow:

Validate Request
      ↓
Authenticate
      ↓
Check Fraud
      ↓
Process Payment
      ↓
Save Transaction
      ↓
Send Notification

Credit Card:

Validate
Authenticate
Fraud Check
Credit Card Processing
Save
Notify

Bank Transfer:

Validate
Authenticate
Fraud Check
Bank Transfer Processing
Save
Notify

The workflow remains the same.

Only certain steps change.


18. Payment Template

public abstract class PaymentProcessor
{
    public void ProcessPayment(decimal amount)
    {
        Validate(amount);
        Authenticate();
        PerformFraudCheck();
        Process(amount);
        SaveTransaction(amount);

        if (ShouldNotify())
        {
            SendNotification();
        }
    }

    protected abstract void Validate(decimal amount);

    protected abstract void Authenticate();

    protected virtual void PerformFraudCheck()
    {
        Console.WriteLine("Performing standard fraud check...");
    }

    protected abstract void Process(decimal amount);

    protected virtual void SaveTransaction(decimal amount)
    {
        Console.WriteLine(
            $"Saving transaction of ${amount}...");
    }

    protected virtual bool ShouldNotify()
    {
        return true;
    }

    protected virtual void SendNotification()
    {
        Console.WriteLine("Payment notification sent.");
    }
}

19. Credit Card Processor

public class CreditCardPaymentProcessor
    : PaymentProcessor
{
    protected override void Validate(decimal amount)
    {
        Console.WriteLine(
            $"Validating credit card payment: ${amount}");
    }

    protected override void Authenticate()
    {
        Console.WriteLine(
            "Authenticating credit card...");
    }

    protected override void Process(decimal amount)
    {
        Console.WriteLine(
            $"Processing credit card payment: ${amount}");
    }
}

20. Bank Transfer Processor

public class BankTransferPaymentProcessor
    : PaymentProcessor
{
    protected override void Validate(decimal amount)
    {
        Console.WriteLine(
            $"Validating bank transfer: ${amount}");
    }

    protected override void Authenticate()
    {
        Console.WriteLine(
            "Authenticating bank account...");
    }

    protected override void Process(decimal amount)
    {
        Console.WriteLine(
            $"Processing bank transfer: ${amount}");
    }
}

Usage:

var creditCard =
    new CreditCardPaymentProcessor();

creditCard.ProcessPayment(500);

Console.WriteLine();

var bankTransfer =
    new BankTransferPaymentProcessor();

bankTransfer.ProcessPayment(1000);

21. ASP.NET Core Implementation

Now let's see how this pattern can be applied to an ASP.NET Core application.

Suppose we are building a document-processing API.

Supported documents:

PDF
CSV
JSON
XML

The processing pipeline is:

Receive Document
      ↓
Validate
      ↓
Parse
      ↓
Transform
      ↓
Save
      ↓
Audit

22. Abstract Document Processor

public abstract class DocumentProcessor
{
    public async Task ProcessAsync(
        Stream document)
    {
        await ValidateAsync(document);

        await ParseAsync(document);

        await TransformAsync();

        await SaveAsync();

        await AuditAsync();
    }

    protected abstract Task ValidateAsync(
        Stream document);

    protected abstract Task ParseAsync(
        Stream document);

    protected abstract Task TransformAsync();

    protected abstract Task SaveAsync();

    protected virtual Task AuditAsync()
    {
        Console.WriteLine(
            "Document processing audited.");

        return Task.CompletedTask;
    }
}

The workflow is centralized.


23. PDF Processor

public class PdfDocumentProcessor
    : DocumentProcessor
{
    protected override Task ValidateAsync(
        Stream document)
    {
        Console.WriteLine(
            "Validating PDF document.");

        return Task.CompletedTask;
    }

    protected override Task ParseAsync(
        Stream document)
    {
        Console.WriteLine(
            "Parsing PDF document.");

        return Task.CompletedTask;
    }

    protected override Task TransformAsync()
    {
        Console.WriteLine(
            "Transforming PDF content.");

        return Task.CompletedTask;
    }

    protected override Task SaveAsync()
    {
        Console.WriteLine(
            "Saving PDF data.");

        return Task.CompletedTask;
    }
}

24. JSON Processor

public class JsonDocumentProcessor
    : DocumentProcessor
{
    protected override Task ValidateAsync(
        Stream document)
    {
        Console.WriteLine(
            "Validating JSON document.");

        return Task.CompletedTask;
    }

    protected override Task ParseAsync(
        Stream document)
    {
        Console.WriteLine(
            "Parsing JSON document.");

        return Task.CompletedTask;
    }

    protected override Task TransformAsync()
    {
        Console.WriteLine(
            "Transforming JSON content.");

        return Task.CompletedTask;
    }

    protected override Task SaveAsync()
    {
        Console.WriteLine(
            "Saving JSON data.");

        return Task.CompletedTask;
    }
}

25. ASP.NET Core Controller

A controller could receive a document and select an appropriate processor.

[ApiController]
[Route("api/documents")]
public class DocumentsController : ControllerBase
{
    [HttpPost("{type}")]
    public async Task<IActionResult> Process(
        string type,
        IFormFile file)
    {
        DocumentProcessor processor;

        if (type.Equals(
                "pdf",
                StringComparison.OrdinalIgnoreCase))
        {
            processor =
                new PdfDocumentProcessor();
        }
        else if (type.Equals(
                "json",
                StringComparison.OrdinalIgnoreCase))
        {
            processor =
                new JsonDocumentProcessor();
        }
        else
        {
            return BadRequest(
                "Unsupported document type.");
        }

        await using var stream =
            file.OpenReadStream();

        await processor.ProcessAsync(stream);

        return Ok(
            "Document processed successfully.");
    }
}

However, in a production ASP.NET Core application, we would generally avoid creating concrete implementations directly inside the controller.

Dependency Injection and a resolver/factory can provide cleaner architecture.


26. Template Method with Dependency Injection

Suppose the base class needs services such as:

ILogger
Repository
Audit Service
Storage Service

We can inject dependencies through constructors.

public abstract class DocumentProcessor
{
    protected readonly ILogger Logger;

    protected DocumentProcessor(
        ILogger logger)
    {
        Logger = logger;
    }

    public async Task ProcessAsync(
        Stream document)
    {
        await ValidateAsync(document);
        await ParseAsync(document);
        await TransformAsync();
        await SaveAsync();
    }

    protected abstract Task ValidateAsync(
        Stream document);

    protected abstract Task ParseAsync(
        Stream document);

    protected abstract Task TransformAsync();

    protected abstract Task SaveAsync();
}

A derived class can receive its own dependencies.

public class PdfDocumentProcessor
    : DocumentProcessor
{
    public PdfDocumentProcessor(
        ILogger<PdfDocumentProcessor> logger)
        : base(logger)
    {
    }

    protected override Task ValidateAsync(
        Stream document)
    {
        Logger.LogInformation(
            "Validating PDF...");

        return Task.CompletedTask;
    }

    protected override Task ParseAsync(
        Stream document)
    {
        Logger.LogInformation(
            "Parsing PDF...");

        return Task.CompletedTask;
    }

    protected override Task TransformAsync()
    {
        Logger.LogInformation(
            "Transforming PDF...");

        return Task.CompletedTask;
    }

    protected override Task SaveAsync()
    {
        Logger.LogInformation(
            "Saving PDF...");

        return Task.CompletedTask;
    }
}

27. Template Method and Dependency Injection – Important Point

The Template Method Pattern itself is based primarily on:

Inheritance
Abstract Classes
Polymorphism

Dependency Injection is complementary.

You can combine them:

ASP.NET Core
      ↓
Dependency Injection
      ↓
Concrete Processor
      ↓
Template Method
      ↓
Overridden Steps

This can be useful in enterprise applications where processing steps require repositories, logging, configuration, APIs, or other infrastructure services.


28. Real-World Enterprise Example – ETL

ETL stands for:

Extract
Transform
Load

A generic ETL workflow might be:

Extract Data
     ↓
Validate
     ↓
Transform
     ↓
Load
     ↓
Audit

Different implementations may extract from:

SQL Server
CSV
REST API
Azure Storage
External Database

Template Method can define:

public void Execute()
{
    Extract();
    Validate();
    Transform();
    Load();
    Audit();
}

while subclasses customize individual operations.


29. Real-World Example – Report Generation

Suppose an enterprise application creates:

Sales Report
Inventory Report
Customer Report
Financial Report

The common workflow may be:

Load Data
    ↓
Validate
    ↓
Prepare Model
    ↓
Generate Report
    ↓
Save Report
    ↓
Notify User

Template Method can define the workflow while each report type implements the specific steps.


30. Real-World Example – Authentication

An authentication pipeline might contain:

Receive Credentials
       ↓
Validate
       ↓
Authenticate
       ↓
Load User
       ↓
Create Session/Token
       ↓
Audit

Different authentication mechanisms might customize the authentication step:

Password
Certificate
External Identity Provider
Corporate Authentication

However, authentication systems often benefit from other patterns and framework abstractions too, so Template Method should be applied only when the workflow genuinely fits inheritance-based customization.


31. Template Method vs Strategy

This is one of the most frequently asked interview questions.

Both patterns can solve algorithm variation, but they do it differently.

Template Method

Uses:

Inheritance

Structure:

Base Class
    ↓
Derived Class

The base class controls the workflow.


Strategy

Uses:

Composition

Structure:

Context
   ↓
Strategy Interface
   ↓
Concrete Strategy

The strategy can be replaced at runtime.

Simple comparison

Template Method
→ Define algorithm skeleton
→ Customize selected steps
→ Inheritance
→ Compile-time class structure

Strategy
→ Encapsulate complete algorithms
→ Replace algorithm independently
→ Composition
→ Runtime interchangeability

32. Template Method vs Factory Method

These two patterns are related.

Factory Method focuses on:

Creating an object.

Template Method focuses on:

Defining the steps of an algorithm.

For example:

Factory Method
      ↓
Create PaymentProcessor

Template Method:

PaymentProcessor
      ↓
Validate
Authenticate
Process
Save
Notify

They can also be used together.


33. Template Method vs State

Template Method

Behavior is organized around an algorithm's steps.

Process
 ↓
Step 1
 ↓
Step 2
 ↓
Step 3

State

Behavior changes based on the object's current state.

Order
 ↓
Pending
 ↓
Confirmed
 ↓
Shipped
 ↓
Delivered

Simple rule:

Template Method controls an algorithm; State controls behavior based on state.


34. Advantages

1. Eliminates Duplicate Workflow Code

The algorithm skeleton is centralized.


2. Controls Algorithm Structure

The base class controls the order of execution.


3. Promotes Code Reuse

Common logic is implemented once.


4. Supports the Open/Closed Principle

The workflow can remain stable while new implementations are added.


5. Encourages Consistency

All derived classes follow the same processing sequence.


6. Supports Hooks

Optional behavior can be customized.


7. Easy to Understand

The algorithm structure is visible in one place.


35. Disadvantages

1. Uses Inheritance

Inheritance introduces coupling between base and derived classes.


2. Can Violate Liskov Substitution Principle if Poorly Designed

Derived classes must genuinely represent valid variations of the base abstraction.


3. Difficult to Change Algorithm Structure Dynamically

If the workflow itself needs runtime replacement, Strategy may be more appropriate.


4. Base Class Can Become Too Large

Too many steps and hooks can make the base class complicated.


5. Inheritance Hierarchies Can Become Deep

Avoid unnecessary levels of inheritance.


36. Best Practices

1. Keep the Template Method Focused

Avoid creating a massive method with dozens of steps.


2. Keep Common Logic in the Base Class

Only common workflow belongs there.


3. Keep Variable Logic in Derived Classes

Don't put concrete implementation details into the base class unnecessarily.


4. Use protected for Extension Points

For example:

protected abstract void Process();

This prevents external callers from invoking individual internal steps directly.


5. Keep the Template Method Stable

Avoid allowing derived classes to completely replace the algorithm sequence.


6. Use Hook Methods Carefully

Hooks are useful for optional behavior, but too many hooks can make the base class difficult to understand.


7. Prefer Composition When Runtime Flexibility Is Required

If algorithms need to change dynamically, consider Strategy.


8. Follow SOLID Principles

Especially:

Single Responsibility
Open/Closed Principle
Liskov Substitution Principle

37. Common Mistakes

Mistake 1 – Making Everything Abstract

Not every method needs to be abstract.

Common behavior should remain in the base class.


Mistake 2 – Allowing Derived Classes to Change the Entire Workflow

The purpose is for the base class to control the algorithm.


Mistake 3 – Too Many Hooks

An excessive number of hooks can make the pattern difficult to understand.


Mistake 4 – Deep Inheritance

Avoid:

Base
 ↓
Level1
 ↓
Level2
 ↓
Level3
 ↓
Level4

Prefer a shallow hierarchy.


Mistake 5 – Using Template Method When Strategy Is Better

If the entire algorithm needs to be interchangeable, Strategy may be a better fit.


38. Unit Testing

The individual derived classes can be tested independently.

For example:

[Fact]
public async Task CsvProcessor_ShouldProcessSuccessfully()
{
    var processor =
        new CsvProcessor();

    processor.Process();

    // Assert expected behavior.
}

For real applications, dependencies such as repositories and external services should be mocked.

For example:

CsvProcessor
    ↓
Repository Mock
    ↓
Storage Mock
    ↓
Logger Mock

This makes individual implementations easier to test.


39. Interview Questions

Beginner

1. What is the Template Method Design Pattern?

It defines the skeleton of an algorithm in a base class and allows subclasses to customize individual steps.


2. Which category does Template Method belong to?

It is a Behavioral Design Pattern.


3. Which OOP concept does it primarily use?

Inheritance and polymorphism.


4. What is a Template Method?

A method in the base class that defines the overall algorithm sequence.


5. What is a hook method?

An optional method that subclasses can override to customize behavior.


Intermediate

6. Why is Template Method often implemented using an abstract class?

Because the pattern requires a common algorithm skeleton and extension points that derived classes can customize.


7. Why should the Template Method generally not be overridden?

Because the base class should control the sequence of the algorithm.


8. What is the difference between abstract methods and hook methods?

Abstract methods require subclasses to provide an implementation.

Hook methods usually provide default behavior and allow optional customization.


9. Template Method vs Strategy?

Template Method uses inheritance.

Strategy uses composition.


10. Template Method vs Factory Method?

Template Method defines an algorithm.

Factory Method defines how an object is created.


Advanced

11. Can Template Method use Dependency Injection?

Yes. Derived classes can receive services through DI while the base class controls the workflow.


12. Does Template Method violate the Open/Closed Principle?

It can support OCP when new implementations extend the base abstraction without changing the existing algorithm.

However, poor inheritance design can create coupling and make changes difficult.


13. How can you prevent subclasses from changing the algorithm?

Keep the Template Method non-overridable and expose only the intended extension points.


14. When would you choose Strategy instead?

Choose Strategy when you need to switch complete algorithms at runtime and want composition rather than inheritance.


15. Can Template Method and Factory Method be used together?

Yes.

A Factory Method can create the appropriate processor, and the Template Method can control how that processor executes its workflow.


16. What are the disadvantages of Template Method?

  • Inheritance coupling

  • Potentially large base classes

  • Deep inheritance hierarchies

  • Limited runtime flexibility


17. What is the Hollywood Principle in Template Method?

A common principle associated with the pattern is:

"Don't call us, we'll call you."

The base class controls the flow and calls the overridden operations at the appropriate points.


18. Why is Template Method called a behavioral pattern?

Because it defines how an algorithm's behavior is organized and how responsibilities are distributed between a base class and subclasses.


40. When Should You Use Template Method?

Use it when:

  • Multiple algorithms follow the same overall workflow.

  • Several classes share common processing steps.

  • The sequence of operations must remain consistent.

  • Some steps vary between implementations.

  • You want to avoid duplicated workflow code.

  • The algorithm structure should be controlled centrally.

  • Inheritance is appropriate for the relationship.

Examples:

Data Import
ETL Processing
Report Generation
Document Processing
Payment Workflows
File Processing
Batch Processing
Order Processing

41. When Should You Avoid Template Method?

Avoid it when:

  • Algorithms are completely unrelated.

  • The complete algorithm needs runtime replacement.

  • Inheritance is not appropriate.

  • The base class would become too complex.

  • There are only minor differences that don't justify an abstraction.

  • Composition would provide a cleaner architecture.

In these situations, consider:

Strategy
Composition
Dependency Injection
Pipeline
Chain of Responsibility

42. Template Method in a Modern .NET Architecture

A practical architecture could look like:

                ASP.NET Core API
                       |
                       ↓
               Application Service
                       |
                       ↓
               Processor Factory
                       |
          +------------+------------+
          |            |            |
          ↓            ↓            ↓
       CSV           JSON          XML
    Processor      Processor      Processor
          \            |            /
           \           |           /
            +----------+----------+
                       |
                 Template Method
                       |
        +--------------+--------------+
        |              |              |
      Validate       Transform       Save

This architecture can be useful when all processors share a stable workflow but differ in individual steps.


43. Template Method vs Strategy – Quick Comparison

FeatureTemplate MethodStrategy
Pattern TypeBehavioralBehavioral
Main GoalDefine algorithm skeletonEncapsulate algorithms
Main MechanismInheritanceComposition
Base ClassUsually requiredNot required
Runtime SwitchingLimitedExcellent
Code ReuseHighModerate
CouplingHigherLower
HooksCommonNot typical
Best ForSame workflow, variable stepsCompletely interchangeable algorithms

44. Simple Memory Trick

Remember:

TEMPLATE METHOD
        ↓
"WHAT ARE THE STEPS?"
        ↓
Base Class controls the workflow

Whereas:

STRATEGY
        ↓
"WHICH ALGORITHM?"
        ↓
Choose an implementation

For example:

Template Method:

Validate
 ↓
Process
 ↓
Save
 ↓
Notify

Strategy:

Process using:

Credit Card
OR
PayPal
OR
Bank Transfer

45. Complete Concept in One Diagram

                 TEMPLATE METHOD
                       |
                       ↓
              AbstractProcessor
                       |
                 Process()
                       |
       +---------------+---------------+
       |               |               |
       ↓               ↓               ↓
    Validate        Process          Save
       |               |               |
       +---------------+---------------+
                       |
                       ↓
            Concrete Processor
                       |
         +-------------+-------------+
         |                           |
         ↓                           ↓
    CsvProcessor              JsonProcessor
         |                           |
    CSV implementation          JSON implementation

The important point is:

The base class defines the workflow; subclasses provide the variable steps.


46. Key Takeaways

Remember these important points:

1. Template Method is a Behavioral Pattern

It focuses on organizing algorithm behavior.

2. It uses inheritance

The base class defines the algorithm and derived classes customize steps.

3. The Template Method defines the algorithm skeleton

For example:

Read
 ↓
Validate
 ↓
Transform
 ↓
Save

4. Abstract methods represent required variation

protected abstract void Process();

5. Hook methods represent optional variation

protected virtual bool ShouldSave()

6. The base class controls the workflow

This keeps implementations consistent.

7. Template Method and Strategy are different

Template Method
→ Same workflow, different steps

Strategy
→ Different interchangeable algorithms

8. Don't overuse inheritance

If composition provides a cleaner solution, consider Strategy or another pattern.


Conclusion

The Template Method Design Pattern is an excellent choice when multiple processes follow the same overall algorithm but have different implementations for individual steps.

Instead of duplicating:

Validate
Process
Save
Notify

across many classes, we define the workflow once:

public void Process()
{
    Validate();
    ProcessData();
    Save();
    Notify();
}

and allow subclasses to customize the individual operations.

The overall architecture becomes:

                 Base Class
                     |
             Template Method
                     |
        +------------+------------+
        |            |            |
        ↓            ↓            ↓
      Step A       Step B       Step C
        ↑            ↑            ↑
        |            |            |
    Concrete      Concrete     Concrete
    Class A       Class B      Class C

This provides code reuse, consistency, extensibility, and centralized workflow management.

However, remember that Template Method relies on inheritance. If your application needs complete algorithms to be dynamically interchangeable, the Strategy Pattern may be a better choice.

The key lesson is:

Use Template Method when the overall algorithm is fixed but certain steps need to vary between implementations.


🚀 Coming Up Next: Part 4.11 – Visitor Design Pattern

In the final article of the Behavioral Design Patterns section, we'll explore the Visitor Design Pattern, including:

  • What is the Visitor Pattern?

  • Why do we need it?

  • Visitor and Element concepts

  • Double Dispatch

  • IVisitor and Accept()

  • UML Class Diagram

  • Complete C# Console Application

  • Document processing example

  • Shopping cart example

  • Tax calculation example

  • ASP.NET Core implementation

  • Real-world enterprise scenarios

  • Advantages and disadvantages

  • Best practices

  • Common mistakes

  • Visitor vs Strategy

  • Visitor vs Composite

  • Visitor vs Interpreter

  • Visitor vs Decorator

  • Interview questions

The Visitor Pattern is particularly useful when you need to perform multiple operations across a stable object structure without repeatedly modifying the classes that represent that structure.

Visitor Design Pattern

Don't Copy

Protected by Copyscape Online Plagiarism Checker