Monday, July 27, 2026

Chain of Responsibility Design Pattern

 


Mastering Design Patterns in C# and ASP.NET Core

Part 4.1 – Chain of Responsibility Design Pattern

Series: Design Patterns in C# and ASP.NET Core
Pattern Category: Behavioral Design Pattern
Difficulty: ⭐⭐⭐⭐☆ Intermediate to Advanced
Prerequisites: C#, OOP, Interfaces, Delegates, Dependency Injection, ASP.NET Core Middleware


Table of Contents

  1. Introduction

  2. What is the Chain of Responsibility Pattern?

  3. Why Do We Need It?

  4. Request and Handler Concepts

  5. How the Handler Chain Works

  6. UML Class Diagram

  7. Components of the Pattern

  8. Complete C# Console Application

  9. Banking Approval Workflow

  10. ASP.NET Core Middleware Example

  11. Exception Handling Pipeline

  12. Validation Pipeline

  13. Authentication and Authorization Pipeline

  14. Real-World Enterprise Examples

  15. Advantages

  16. Disadvantages

  17. Best Practices

  18. Common Mistakes

  19. Chain of Responsibility vs Middleware

  20. Chain of Responsibility vs Decorator

  21. Chain of Responsibility vs Other Patterns

  22. Interview Questions

  23. Summary

  24. Coming Up Next


1. Introduction

In enterprise applications, a request often needs to pass through multiple processing steps before it reaches its final destination.

For example, consider an ASP.NET Core Web API request:

HTTP Request
     ↓
Exception Handling
     ↓
Logging
     ↓
Authentication
     ↓
Authorization
     ↓
Validation
     ↓
Business Logic
     ↓
Database
     ↓
HTTP Response

Each component has a specific responsibility.

The important question is:

How can we organize these processing steps without putting everything into one large class?

This is one of the problems solved by the Chain of Responsibility Design Pattern.


2. What is the Chain of Responsibility Pattern?

Definition

The Chain of Responsibility Design Pattern is a behavioral design pattern that allows a request to pass through a sequence of handlers.

Each handler:

  1. Receives the request.

  2. Decides whether it should process it.

  3. Performs its responsibility if appropriate.

  4. Optionally passes the request to the next handler.

In simple terms:

Pass a request through a chain of objects until the appropriate handler processes it.

The basic structure is:

Client
   ↓
Handler 1
   ↓
Handler 2
   ↓
Handler 3
   ↓
Handler 4

3. Why Do We Need It?

Imagine an order-processing application.

Without Chain of Responsibility, you might write:

public void ProcessOrder(Order order)
{
    ValidateOrder(order);

    CheckAuthentication(order);

    CheckAuthorization(order);

    CheckInventory(order);

    ProcessPayment(order);

    SendNotification(order);

    SaveAuditLog(order);
}

This class is now responsible for many unrelated tasks.

Problems include:

  • Large methods

  • Tight coupling

  • Difficult testing

  • Difficult modification

  • Difficult reordering

  • Difficult reuse

  • Violates Single Responsibility Principle

With a chain:

Request
   ↓
Validation Handler
   ↓
Authentication Handler
   ↓
Authorization Handler
   ↓
Inventory Handler
   ↓
Payment Handler

Each handler has one primary responsibility.


4. Request and Handler Concepts

The pattern has two important concepts:

Request

The information that needs to be processed.

Example:

public class OrderRequest
{
    public int OrderId { get; set; }

    public decimal Amount { get; set; }

    public string UserRole { get; set; } = string.Empty;
}

Handler

A component responsible for processing some aspect of the request.

For example:

ValidationHandler
AuthenticationHandler
AuthorizationHandler
PaymentHandler

Each handler can decide whether to continue the request.


5. How the Handler Chain Works

Suppose we have:

Client
  ↓
ValidationHandler
  ↓
AuthenticationHandler
  ↓
AuthorizationHandler
  ↓
PaymentHandler

The request travels through the chain:

Request
   ↓
Validation
   ↓
Valid?
 ├── No → Stop
 └── Yes
       ↓
Authentication
       ↓
Authenticated?
 ├── No → Stop
 └── Yes
       ↓
Authorization
       ↓
Authorized?
 ├── No → Stop
 └── Yes
       ↓
Payment
       ↓
Completed

This gives us an important capability:

A handler can stop the chain when the request cannot continue.


6. UML Class Diagram

A typical Chain of Responsibility structure looks like this:

                     +------------------+
                     |     Handler      |
                     +------------------+
                     | - nextHandler    |
                     +------------------+
                     | + SetNext()      |
                     | + Handle()       |
                     +--------^---------+
                              |
                +-------------+-------------+
                |             |             |
                |             |             |
       +----------------+ +----------------+ +----------------+
       | Validation     | | Authorization  | | Payment        |
       | Handler        | | Handler        | | Handler        |
       +----------------+ +----------------+ +----------------+
       | Handle()       | | Handle()       | | Handle()       |
       +----------------+ +----------------+ +----------------+

                              ↑
                              |
                            Client

7. Components of the Pattern

The main components are:

1. Client

Creates and sends the request.

2. Handler

Defines how handlers process requests and pass them onward.

3. Concrete Handler

Performs a specific responsibility.

4. Successor

The next handler in the chain.

Handler A
   ↓
Handler B
   ↓
Handler C

8. Complete C# Console Application

Let's build a complete example using an expense approval workflow.

Suppose employees submit expenses.

Different approval levels are required depending on the amount:

$0 – $1,000
    ↓
Manager

$1,001 – $5,000
    ↓
Director

Above $5,000
    ↓
VP

Step 1 – Request

public class ExpenseRequest
{
    public string EmployeeName { get; set; } = string.Empty;

    public decimal Amount { get; set; }
}

Step 2 – Handler Interface

public interface IExpenseApprover
{
    IExpenseApprover SetNext(IExpenseApprover next);

    void Approve(ExpenseRequest request);
}

Step 3 – Manager Handler

public class ManagerApprover : IExpenseApprover
{
    private IExpenseApprover? _next;

    public IExpenseApprover SetNext(IExpenseApprover next)
    {
        _next = next;
        return next;
    }

    public void Approve(ExpenseRequest request)
    {
        if (request.Amount <= 1000)
        {
            Console.WriteLine(
                $"Manager approved ${request.Amount}.");
            return;
        }

        _next?.Approve(request);
    }
}

Step 4 – Director Handler

public class DirectorApprover : IExpenseApprover
{
    private IExpenseApprover? _next;

    public IExpenseApprover SetNext(IExpenseApprover next)
    {
        _next = next;
        return next;
    }

    public void Approve(ExpenseRequest request)
    {
        if (request.Amount <= 5000)
        {
            Console.WriteLine(
                $"Director approved ${request.Amount}.");
            return;
        }

        _next?.Approve(request);
    }
}

Step 5 – VP Handler

public class VicePresidentApprover : IExpenseApprover
{
    public IExpenseApprover SetNext(IExpenseApprover next)
    {
        return next;
    }

    public void Approve(ExpenseRequest request)
    {
        Console.WriteLine(
            $"VP approved ${request.Amount}.");
    }
}

Step 6 – Client

class Program
{
    static void Main()
    {
        var manager = new ManagerApprover();
        var director = new DirectorApprover();
        var vp = new VicePresidentApprover();

        manager.SetNext(director)
               .SetNext(vp);

        var request1 = new ExpenseRequest
        {
            EmployeeName = "John",
            Amount = 500
        };

        var request2 = new ExpenseRequest
        {
            EmployeeName = "Mary",
            Amount = 3000
        };

        var request3 = new ExpenseRequest
        {
            EmployeeName = "David",
            Amount = 10000
        };

        manager.Approve(request1);
        manager.Approve(request2);
        manager.Approve(request3);
    }
}

Output

Manager approved $500.
Director approved $3000.
VP approved $10000.

The client doesn't need to determine which approver should process the request.

The chain handles that responsibility.


9. Banking Approval Workflow

The Chain of Responsibility Pattern is particularly useful for banking workflows.

Imagine a loan approval process:

Loan Request
      ↓
Initial Validation
      ↓
Credit Score Check
      ↓
Manager Approval
      ↓
Branch Manager
      ↓
Regional Manager
      ↓
Risk Department

Different loan amounts may require different approval levels.

Loan ≤ $10,000
     ↓
Officer

Loan ≤ $50,000
     ↓
Manager

Loan ≤ $250,000
     ↓
Regional Manager

Loan > $250,000
     ↓
Risk Committee

The client simply submits:

loanChain.Process(loanRequest);

The chain decides who should process the request.


10. ASP.NET Core Middleware Example

This is one of the most important applications of the Chain of Responsibility concept.

ASP.NET Core middleware forms a request-processing pipeline.

For example:

HTTP Request
     ↓
Exception Middleware
     ↓
Logging Middleware
     ↓
Authentication Middleware
     ↓
Authorization Middleware
     ↓
Routing
     ↓
Endpoint

Each middleware can perform work before and after calling the next component.


Custom Middleware

public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;

    public RequestLoggingMiddleware(
        RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(
        HttpContext context)
    {
        Console.WriteLine(
            $"Request: {context.Request.Path}");

        await _next(context);

        Console.WriteLine(
            $"Response: {context.Response.StatusCode}");
    }
}

Register Middleware

In Program.cs:

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.UseMiddleware<RequestLoggingMiddleware>();

app.MapControllers();

app.Run();

The important part is:

await _next(context);

This forwards the request to the next component in the pipeline.


11. Exception Handling Pipeline

A common ASP.NET Core pattern is to place exception handling near the beginning of the pipeline.

Conceptually:

Request
   ↓
Exception Handler
   ↓
Logging
   ↓
Authentication
   ↓
Authorization
   ↓
Controller

Example:

public class ExceptionHandlingMiddleware
{
    private readonly RequestDelegate _next;

    public ExceptionHandlingMiddleware(
        RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(
        HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);

            context.Response.StatusCode = 500;

            await context.Response.WriteAsync(
                "An unexpected error occurred.");
        }
    }
}

The middleware can catch exceptions generated by downstream components.


12. Validation Pipeline

Validation is another excellent use case.

For example:

Request
   ↓
Required Field Validation
   ↓
Format Validation
   ↓
Business Validation
   ↓
Authorization
   ↓
Process Request

A handler could look like:

public interface IRequestHandler
{
    IRequestHandler SetNext(IRequestHandler next);

    bool Handle(OrderRequest request);
}

A validation handler might stop the chain:

public class OrderValidationHandler : IRequestHandler
{
    private IRequestHandler? _next;

    public IRequestHandler SetNext(
        IRequestHandler next)
    {
        _next = next;
        return next;
    }

    public bool Handle(OrderRequest request)
    {
        if (request.OrderId <= 0)
        {
            Console.WriteLine(
                "Invalid Order ID.");

            return false;
        }

        return _next?.Handle(request) ?? true;
    }
}

The chain stops if validation fails.


13. Authentication and Authorization Pipeline

Authentication and authorization can also be understood through the chain concept.

HTTP Request
      ↓
Authentication
      ↓
Is User Authenticated?
      ↓
Authorization
      ↓
Is User Authorized?
      ↓
Endpoint

For example:

Request
   ↓
Authentication Handler
   ↓
Authorization Handler
   ↓
Role Handler
   ↓
Endpoint

A failed security check can terminate the request before it reaches the business logic.


14. Real-World Enterprise Examples

The Chain of Responsibility Pattern is useful in many enterprise scenarios.

Banking

  • Loan approval

  • Payment authorization

  • Fraud checks

  • Transaction validation

  • Risk assessment

E-Commerce

  • Order validation

  • Discount validation

  • Inventory checks

  • Payment verification

  • Shipping validation

ASP.NET Core

  • Middleware pipeline

  • Authentication

  • Authorization

  • Exception handling

  • Request logging

Customer Support

Support Agent
     ↓
Team Lead
     ↓
Manager
     ↓
Director

Document Approval

Employee
    ↓
Manager
    ↓
Department Head
    ↓
Director
    ↓
Executive

Validation

Basic Validation
      ↓
Business Validation
      ↓
Security Validation
      ↓
Database Validation

15. Advantages

1. Reduces Coupling

The sender doesn't need to know which handler will process the request.


2. Supports Single Responsibility

Each handler can focus on one task.


3. Easy to Add New Handlers

You can add another handler without heavily modifying existing handlers.


4. Flexible Processing Order

Handlers can be reordered.

For example:

Logging
 ↓
Authentication
 ↓
Validation

could become:

Authentication
 ↓
Logging
 ↓
Validation

depending on requirements.


5. Supports Early Termination

A handler can stop processing when a condition fails.


6. Good for Pipelines

It is naturally suited to sequential processing.


16. Disadvantages

1. Request May Not Be Handled

If no handler processes the request, the request can reach the end of the chain without being handled.


2. Debugging Can Be More Difficult

The request may pass through many classes.

Handler A
 ↓
Handler B
 ↓
Handler C
 ↓
Handler D

Finding where something went wrong may require tracing the chain.


3. Long Chains

A very long chain can become difficult to understand.


4. Order Matters

Changing handler order can change application behavior.

For example:

Authentication → Authorization

is generally very different from:

Authorization → Authentication

5. Runtime Overhead

Every request may pass through multiple handlers.


17. Best Practices

1. Keep Each Handler Focused

Follow the Single Responsibility Principle.

Good:

ValidationHandler
AuthenticationHandler
LoggingHandler

Avoid:

EverythingHandler

2. Make Handler Ordering Explicit

Document the expected order.


3. Define Clear Termination Rules

Every chain should clearly define:

  • When processing stops

  • When processing continues

  • What happens if nobody handles the request


4. Keep Handlers Independent

Handlers should avoid unnecessary knowledge about other handlers.


5. Use Dependency Injection

In ASP.NET Core, handlers can be registered through DI.

builder.Services.AddScoped<ValidationHandler>();
builder.Services.AddScoped<AuthorizationHandler>();
builder.Services.AddScoped<PaymentHandler>();

6. Keep Chains Testable

Each handler should be unit-testable independently.


7. Don't Create Chains Just for the Sake of Patterns

If you have only one simple operation, a chain may introduce unnecessary complexity.


18. Common Mistakes

Mistake 1 – Creating a Giant Handler

A handler should not contain unrelated responsibilities.


Mistake 2 – Hard-Coding the Entire Chain

Avoid tightly coupling every handler to specific implementations.


Mistake 3 – Forgetting to Call the Next Handler

A handler that should continue the chain must invoke the successor.

For example:

_next?.Handle(request);

Mistake 4 – No Termination Strategy

Always determine what happens if:

No handler handles the request

Mistake 5 – Wrong Handler Ordering

Ordering is especially important in:

  • Authentication

  • Authorization

  • Validation

  • Middleware


19. Chain of Responsibility vs Middleware

ASP.NET Core Middleware and Chain of Responsibility have very similar concepts, but they aren't identical.

FeatureChain of ResponsibilityASP.NET Core Middleware
CategoryBehavioral Design PatternFramework request pipeline
PurposePass request through handlersProcess HTTP requests
HandlerCustom handlerMiddleware
Next ComponentSuccessorRequestDelegate
Can Stop Chain?YesYes
Can Continue?YesYes
HTTP-specificNoYes
Framework Built-inNoYes

Conceptual similarity

Chain of Responsibility:

Handler
   ↓
Next Handler

ASP.NET Core:

Middleware
   ↓
RequestDelegate
   ↓
Next Middleware

The concepts are closely related, but ASP.NET Core Middleware is a framework mechanism rather than simply a direct implementation of the GoF pattern.


20. Chain of Responsibility vs Decorator

These patterns can look similar because both may wrap or forward to another object.

But their intent is different.

FeatureChain of ResponsibilityDecorator
Main GoalProcess a request through handlersAdd responsibilities
Number of HandlersPotentially manyPotentially many
Can Stop ProcessingYesUsually forwards
Handler SelectionBased on request/conditionsUsually predetermined
Primary ConceptRequest processingBehavior extension

Example

Chain:

Request
 ↓
Handler A
 ↓
Handler B
 ↓
Handler C

Decorator:

Client
 ↓
Logging Decorator
 ↓
Caching Decorator
 ↓
Real Service

The distinction is based primarily on intent, not merely class structure.


21. Chain of Responsibility vs Other Patterns

PatternPrimary Question
Chain of ResponsibilityWho should process this request?
StrategyWhich algorithm should I use?
CommandHow can I represent this operation as an object?
MediatorHow can objects communicate through a central coordinator?
ObserverWho should be notified when something changes?
DecoratorHow can I add behavior to an object?

This is a useful way to identify patterns during architecture discussions and interviews.


22. Interview Questions

Beginner Level

1. What is the Chain of Responsibility Pattern?

It is a behavioral pattern where a request is passed through a sequence of handlers until it is handled or reaches the end of the chain.


2. What are the main components?

Typically:

  • Client

  • Handler

  • Concrete Handler

  • Successor


3. Can multiple handlers process the same request?

Yes.

The pattern can be designed so that:

Handler A → Handler B → Handler C

all perform processing before the request completes.

Alternatively, a handler can stop the chain once it handles the request.


4. Can a handler stop the chain?

Yes.

For example:

if (!IsValid(request))
{
    return;
}

Intermediate Level

5. What are real-world examples?

Examples include:

  • ASP.NET Core Middleware

  • Approval workflows

  • Validation pipelines

  • Authentication/authorization

  • Exception handling

  • Support escalation

  • Payment validation


6. What is the main benefit?

The primary benefit is reduced coupling between the request sender and the specific object responsible for handling it.


7. What happens if no handler processes the request?

The request reaches the end of the chain.

The application should explicitly decide what happens in that situation, such as:

  • Return an error

  • Reject the request

  • Use a default handler

  • Log the problem


8. How does Chain of Responsibility support SOLID?

It can support:

  • Single Responsibility Principle: Each handler performs one responsibility.

  • Open/Closed Principle: New handlers can be added without heavily modifying existing ones.

  • Dependency Inversion: Handlers can depend on abstractions.


Advanced Level

9. Is ASP.NET Core Middleware exactly the Chain of Responsibility Pattern?

Not exactly.

ASP.NET Core Middleware implements a pipeline concept that is closely related to Chain of Responsibility.

Middleware uses RequestDelegate to pass execution to the next middleware component.


10. What is the difference between Chain of Responsibility and Strategy?

Chain of Responsibility determines how a request passes through handlers.

Strategy selects one algorithm or behavior from a set of alternatives.


11. What is the difference between Chain of Responsibility and Mediator?

Chain of Responsibility passes a request sequentially through handlers.

Mediator centralizes communication among multiple objects.


12. Can Chain of Responsibility be used in microservices?

Yes.

It can be useful for:

  • Request validation

  • Authentication

  • Authorization

  • Fraud detection

  • Message processing

  • Event processing

  • API request pipelines


13. What are the disadvantages?

Common disadvantages include:

  • Long chains

  • Debugging complexity

  • Runtime overhead

  • Handler ordering problems

  • Possibility of an unhandled request


23. Summary

The Chain of Responsibility Design Pattern provides a flexible way to process requests through multiple handlers.

The core concept is:

             Request
                ↓
        +---------------+
        |   Handler 1   |
        +---------------+
                ↓
        +---------------+
        |   Handler 2   |
        +---------------+
                ↓
        +---------------+
        |   Handler 3   |
        +---------------+
                ↓
              Result

Each handler can:

Process
   ↓
Stop

OR

Process
   ↓
Pass to Next

The pattern is particularly useful for:

  • Validation pipelines

  • Approval workflows

  • Authentication

  • Authorization

  • Exception handling

  • Logging

  • Request processing

  • ASP.NET Core Middleware

  • Enterprise workflows

The most important idea to remember is:

Chain of Responsibility separates request processing into independent handlers and allows the request to move through those handlers without tightly coupling the sender to a particular handler.


Behavioral Design Patterns – Progress

We have now started the Behavioral Design Patterns section:

PartPatternStatus
4.1Chain of Responsibility✅ Completed
4.2CommandNext
4.3InterpreterUpcoming
4.4IteratorUpcoming
4.5MediatorUpcoming
4.6MementoUpcoming
4.7ObserverUpcoming
4.8StateUpcoming
4.9StrategyUpcoming
4.10Template MethodUpcoming
4.11VisitorUpcoming

🚀 Coming Up Next: Part 4.2 – Command Design Pattern

In the next article, we'll explore the Command Design Pattern, including:

  • What is the Command Pattern?

  • Why do we need it?

  • Command, Receiver, Invoker, and Client

  • UML Class Diagram

  • Complete C# Console Application

  • ASP.NET Core implementation

  • Command and Handler architecture

  • CQRS connection

  • Undo/Redo functionality

  • Queue and background processing

  • Banking transaction example

  • E-commerce order example

  • Real-world enterprise scenarios

  • Advantages and disadvantages

  • Best practices

  • Common mistakes

  • Command vs Strategy

  • Command vs Mediator

  • Command vs Chain of Responsibility

  • Interview questions

The Command Pattern will be especially useful for understanding CQRS, background jobs, message queues, undo/redo operations, and clean command-handler architectures in modern .NET applications.

Behavioral Design Patterns

 


Mastering Design Patterns in C# and ASP.NET Core

Part 4 – Behavioral Design Patterns

Series: Design Patterns in C# and ASP.NET Core
Category: Behavioral Design Patterns
Level: Intermediate to Advanced
Prerequisites: C#, OOP, SOLID Principles, Interfaces, Dependency Injection, ASP.NET Core


Introduction

We have completed the Creational Design Patterns and Structural Design Patterns sections.

Now we move to the third major category of the Gang of Four (GoF) Design Patterns:

Behavioral Design Patterns

Behavioral Design Patterns focus on how objects communicate with each other, how responsibilities are distributed, and how algorithms or workflows can change without heavily modifying existing code.

While:

  • Creational patterns focus on how objects are created

  • Structural patterns focus on how objects and classes are composed

  • Behavioral patterns focus on how objects communicate and behave

A simple way to remember them:

CREATIONAL
     ↓
How do I create objects?

STRUCTURAL
     ↓
How do I combine objects?

BEHAVIORAL
     ↓
How do objects communicate?

Why Do We Need Behavioral Design Patterns?

Consider a large enterprise application.

A request might travel through:

Controller
   ↓
Authentication
   ↓
Authorization
   ↓
Validation
   ↓
Business Logic
   ↓
Payment
   ↓
Notification
   ↓
Audit

If all of this logic is placed into one class, the application becomes difficult to maintain.

For example:

public void ProcessOrder()
{
    // Authentication

    // Authorization

    // Validation

    // Payment

    // Inventory

    // Notification

    // Logging

    // Audit
}

This creates several problems:

  • Large classes

  • Tight coupling

  • Difficult testing

  • Difficult maintenance

  • Hard-to-change business rules

  • Repeated code

  • Difficult extension

Behavioral patterns help us distribute these responsibilities more effectively.


Behavioral Patterns We Will Cover

Our series will cover the following 11 behavioral patterns:

PartPatternMain Purpose
4.1Chain of ResponsibilityPass a request through a chain of handlers
4.2CommandEncapsulate a request as an object
4.3InterpreterDefine and evaluate a language or expression
4.4IteratorTraverse a collection without exposing its internal structure
4.5MediatorCentralize communication between objects
4.6MementoCapture and restore an object's state
4.7ObserverNotify dependent objects when state changes
4.8StateChange behavior when an object's state changes
4.9StrategySwitch algorithms or behaviors dynamically
4.10Template MethodDefine an algorithm skeleton while allowing steps to vary
4.11VisitorAdd operations to object structures without modifying their classes

4.1 – Chain of Responsibility

Purpose

The Chain of Responsibility Pattern allows a request to pass through a chain of handlers until one of them handles it.

Example

An ASP.NET Core request pipeline is conceptually similar to this idea:

Request
   ↓
Logging
   ↓
Authentication
   ↓
Authorization
   ↓
Validation
   ↓
Controller

Each component can process the request or pass it to the next component.

We'll build examples such as:

  • Approval workflows

  • Validation pipelines

  • Support ticket escalation

  • Authorization

  • Middleware-like processing


4.2 – Command

Purpose

The Command Pattern encapsulates a request as an object.

Instead of:

service.CreateOrder();

we can represent the operation as:

CreateOrderCommand

This is particularly useful in:

  • CQRS

  • Undo/Redo

  • Queues

  • Background jobs

  • Transaction processing

  • Event-driven applications

Example:

Controller
    ↓
Command
    ↓
Command Handler
    ↓
Business Logic

4.3 – Interpreter

Purpose

The Interpreter Pattern defines a representation for a language or grammar and provides an interpreter for that language.

For example:

age > 18

or:

salary > 50000 AND experience > 5

The application can interpret these expressions dynamically.

Potential applications include:

  • Rule engines

  • Search expressions

  • Filtering

  • Query languages

  • Business rules

  • Expression evaluation


4.4 – Iterator

Purpose

The Iterator Pattern provides a standard way to traverse a collection without exposing its internal representation.

C# already provides excellent support for iterator concepts through:

IEnumerable<T>
IEnumerator<T>
yield return

For example:

foreach (var customer in customers)
{
    Console.WriteLine(customer.Name);
}

The client doesn't need to know whether the data is stored in:

  • Array

  • List

  • Tree

  • Database result

  • Custom collection

We'll explore how this pattern works internally.


4.5 – Mediator

Purpose

The Mediator Pattern centralizes communication between objects.

Instead of:

A → B
A → C
A → D
B → C
B → D
C → D

we introduce a mediator:

          A
          |
          |
B ---- Mediator ---- C
          |
          |
          D

This reduces direct dependencies between components.

A very important real-world example is:

ASP.NET Core Controller
        ↓
      Mediator
        ↓
   Command Handler
        ↓
      Service

This pattern is especially relevant when discussing MediatR-style architectures and CQRS.


4.6 – Memento

Purpose

The Memento Pattern captures an object's state so it can be restored later.

The classic example is:

Document
   ↓
Edit
   ↓
Save State
   ↓
Edit Again
   ↓
Undo
   ↓
Restore Previous State

Real-world applications include:

  • Undo/Redo

  • Draft saving

  • Transaction rollback concepts

  • Game checkpoints

  • Editor history


4.7 – Observer

Purpose

The Observer Pattern establishes a one-to-many relationship where multiple objects are notified when another object's state changes.

Example:

                  Order Created
                       ↓
                    Subject
                       |
          +------------+------------+
          ↓            ↓            ↓
       Email        SMS          Audit
       Service      Service       Service

This is highly relevant to:

  • Event-driven systems

  • Notifications

  • Domain events

  • UI updates

  • Messaging systems

C# provides related mechanisms through:

event
Action<T>
IObservable<T>
IObserver<T>

4.8 – State

Purpose

The State Pattern allows an object to change its behavior when its internal state changes.

For example, an order could have:

Pending
   ↓
Confirmed
   ↓
Shipped
   ↓
Delivered

Different operations may be allowed depending on the current state.

Instead of:

if (status == "Pending")
{
    ...
}
else if (status == "Confirmed")
{
    ...
}
else if (status == "Shipped")
{
    ...
}

we can represent each state using a separate class.


4.9 – Strategy

Purpose

The Strategy Pattern allows us to define multiple algorithms and select the appropriate algorithm at runtime.

For example, an e-commerce application may support:

Payment
 ├── Credit Card
 ├── Debit Card
 ├── Bank Transfer
 └── Digital Wallet

Instead of:

if (paymentType == "Card")
{
    // Card
}
else if (paymentType == "Bank")
{
    // Bank
}
else if (paymentType == "Wallet")
{
    // Wallet
}

we can use:

IPaymentStrategy
       ↑
       |
+------+-------+---------+
|              |         |
Card         Bank      Wallet
Strategy     Strategy   Strategy

This is one of the most useful patterns in enterprise C# development.


4.10 – Template Method

Purpose

The Template Method Pattern defines the skeleton of an algorithm while allowing subclasses to customize certain steps.

For example:

Process Application
       ↓
Validate
       ↓
Load Data
       ↓
Process
       ↓
Save
       ↓
Notify

The overall algorithm stays the same, while individual steps can differ.

This pattern is useful when multiple workflows follow the same general process but have different implementations for specific steps.


4.11 – Visitor

Purpose

The Visitor Pattern allows us to add new operations to an object structure without modifying the classes that make up the structure.

It is particularly useful for complex object structures.

For example:

Document
 ├── Paragraph
 ├── Image
 ├── Table
 └── Chart

We might want to perform different operations:

PDF Export
Word Export
Validation
Statistics
Security Scan

Rather than adding all these operations directly into every document class, the Visitor Pattern separates them.


Behavioral Patterns – Enterprise Examples

Let's look at where these patterns could appear in a modern .NET enterprise application.

                         ASP.NET Core API
                                |
                                ↓
                       Chain of Responsibility
                                |
                                ↓
                            Mediator
                                |
                   +------------+------------+
                   ↓                         ↓
                Command                   Query
                   ↓                         ↓
              Handler                    Handler
                   |
                   ↓
                Strategy
                   |
          +--------+--------+
          ↓                 ↓
       Payment          Notification
       Strategy           Strategy
          |
          ↓
         State
          |
    Order Lifecycle

This illustrates how multiple behavioral patterns can work together.


Behavioral Patterns and SOLID Principles

Behavioral patterns often complement SOLID principles.

SOLID PrincipleRelated Behavioral Patterns
Single ResponsibilityCommand, Strategy
Open/ClosedStrategy, State, Visitor
Liskov SubstitutionStrategy, State
Interface SegregationCommand, Strategy
Dependency InversionMediator, Strategy, Observer

These patterns aren't replacements for SOLID.

Instead:

SOLID provides design principles, while Design Patterns provide proven approaches for applying those principles to recurring problems.


Behavioral Patterns in Modern .NET

Modern .NET provides many built-in mechanisms that resemble or implement ideas from behavioral patterns.

Examples include:

Middleware

Conceptually related to:

Chain of Responsibility

Request
 ↓
Middleware
 ↓
Middleware
 ↓
Middleware
 ↓
Endpoint

Delegates and Events

Related to:

Observer

public event EventHandler? OrderCreated;

IEnumerable

Related to:

Iterator

IEnumerable<Customer>

Dependency Injection

Can help implement:

Strategy

services.AddScoped<IPaymentStrategy, CreditCardStrategy>();

CQRS

Frequently uses ideas related to:

Command + Mediator

Command
   ↓
Mediator
   ↓
Handler

Behavioral Design Patterns – Learning Approach

For every pattern in Part 4, we will follow the same structure.

1. Definition

We'll understand exactly what the pattern is and the problem it solves.

2. Problem Statement

We'll first understand the problem before introducing the pattern.

3. Why Do We Need It?

We'll identify the design problems that make the pattern useful.

4. UML Class Diagram

We'll visualize the relationships between:

  • Interfaces

  • Classes

  • Handlers

  • Clients

  • Contexts

5. Complete C# Console Application

Every pattern will include a runnable example.

6. ASP.NET Core Implementation

We'll then transform the concept into an enterprise-style ASP.NET Core example.

7. Real-World Example

We'll use practical examples such as:

  • Banking

  • E-commerce

  • Payment processing

  • Order management

  • Notifications

  • Authentication

  • Microservices

8. Advantages

We'll discuss when the pattern provides value.

9. Disadvantages

We'll also discuss when the pattern should not be used.

10. Best Practices

We'll cover modern C# and ASP.NET Core recommendations.

11. Common Mistakes

We'll identify common implementation problems.

12. Interview Questions

Each article will finish with interview questions ranging from beginner to architect level.


Behavioral Design Patterns – Roadmap

PART 4
│
├── 4.1 Chain of Responsibility
│
├── 4.2 Command
│
├── 4.3 Interpreter
│
├── 4.4 Iterator
│
├── 4.5 Mediator
│
├── 4.6 Memento
│
├── 4.7 Observer
│
├── 4.8 State
│
├── 4.9 Strategy
│
├── 4.10 Template Method
│
└── 4.11 Visitor

Complete Design Patterns Series Roadmap

At this stage, our complete series looks like this:

Design Patterns in C# and ASP.NET Core
│
├── Part 1 – Introduction
│
├── Part 2 – Creational Patterns
│   ├── Singleton
│   ├── Factory Method
│   ├── Abstract Factory
│   ├── Builder
│   └── Prototype
│
├── Part 3 – Structural Patterns
│   ├── Adapter
│   ├── Bridge
│   ├── Composite
│   ├── Decorator
│   ├── Facade
│   ├── Flyweight
│   └── Proxy
│
└── Part 4 – Behavioral Patterns
    ├── Chain of Responsibility
    ├── Command
    ├── Interpreter
    ├── Iterator
    ├── Mediator
    ├── Memento
    ├── Observer
    ├── State
    ├── Strategy
    ├── Template Method
    └── Visitor

Final Thoughts

Behavioral Design Patterns are particularly important when developing large-scale .NET applications, because enterprise systems frequently contain complex workflows, business rules, communication between services, event handling, and changing algorithms.

The most important thing is not to memorize all 11 patterns.

Instead, learn to recognize the problem:

"What kind of communication or responsibility problem am I trying to solve?"

Then select the appropriate pattern.

For example:

Request needs multiple handlers?
        ↓
Chain of Responsibility

Operation should be represented as an object?
        ↓
Command

Objects need centralized communication?
        ↓
Mediator

Objects need notifications?
        ↓
Observer

Behavior changes according to state?
        ↓
State

Algorithm needs to be interchangeable?
        ↓
Strategy

Same workflow, customizable steps?
        ↓
Template Method

Need to add operations without modifying object structure?
        ↓
Visitor

This problem-first approach is much more valuable in real-world development and .NET architecture interviews than simply memorizing pattern definitions.


🚀 Next Article

Part 4.1 – Chain of Responsibility Design Pattern

In the next article, we'll start with the Chain of Responsibility Design Pattern, covering:

  • What is the Chain of Responsibility Pattern?

  • Why do we need it?

  • Request and Handler concepts

  • Handler chains

  • UML Class Diagram

  • Complete C# Console Application

  • ASP.NET Core Middleware example

  • Banking approval workflow

  • Exception handling pipeline

  • Validation pipeline

  • Authentication and authorization pipeline

  • Real-world enterprise examples

  • Advantages and disadvantages

  • Best practices

  • Common mistakes

  • Chain of Responsibility vs Middleware

  • Chain of Responsibility vs Decorator

  • Interview questions

The next article will be particularly useful for understanding how ASP.NET Core Middleware works internally and how request-processing pipelines can be designed using the Chain of Responsibility concept.

Don't Copy

Protected by Copyscape Online Plagiarism Checker