Chain of Responsibility Design Pattern in C# with Example

1. Definition

The Chain of Responsibility Design Pattern decouples request senders from receivers by allowing more than one object to handle a request. These handlers are chained together, and the request is passed along the chain until it's processed or the end of the chain is reached.

2. Problem Statement

Imagine you have multiple layers of approval in an organization for certain requests. Instead of hardcoding a fixed sequence of calls to these approvers or using complex conditional logic, how can you efficiently manage this workflow?

3. Solution

The Chain of Responsibility pattern offers a solution by allowing multiple objects to attempt to handle a request. If an object can't handle the request, it forwards it to the next handler in the chain.

4. Real-World Use Cases

1. Event handling in GUI components.

2. Middleware in web server frameworks.

3. Approval processes in organizations.

5. Implementation Steps

1. Define the handler interface which declares a method for handling requests and (optionally) setting a successor.

2. Create concrete handler classes implementing the handler interface.

3. Chain these handlers together in the desired sequence.

6. Implementation in C#

// Step 1: Define the handler interface
public interface IHandler
{
    IHandler SetNext(IHandler handler);
    void HandleRequest(string request);
}

// Step 2: Implement concrete handlers
public class Manager : IHandler
{
    private IHandler _nextHandler;

    public IHandler SetNext(IHandler handler)
    {
        _nextHandler = handler;
        return _nextHandler;
    }

    public void HandleRequest(string request)
    {
        if (request == "Vacation")
        {
            Console.WriteLine("Manager approves the vacation.");
        }
        else
        {
            _nextHandler?.HandleRequest(request);
        }
    }
}

public class Director : IHandler
{
    private IHandler _nextHandler;

    public IHandler SetNext(IHandler handler)
    {
        _nextHandler = handler;
        return _nextHandler;
    }

    public void HandleRequest(string request)
    {
        if (request == "Raise")
        {
            Console.WriteLine("Director approves the raise.");
        }
        else
        {
            _nextHandler?.HandleRequest(request);
        }
    }
}

public class Program
{
    public static void Main()
    {
        IHandler manager = new Manager();
        IHandler director = new Director();

        manager.SetNext(director);

        Console.WriteLine("Client: Sending 'Vacation' request...");
        manager.HandleRequest("Vacation");

        Console.WriteLine("Client: Sending 'Raise' request...");
        manager.HandleRequest("Raise");
    }
}

Output:

Client: Sending 'Vacation' request...
Manager approves the vacation.
Client: Sending 'Raise' request...
Director approves the raise.

Explanation:

The Manager and Director are handlers that are part of the approval chain. 

When a request for a "Vacation" comes, the Manager handles it. 

When a request for a "Raise" comes, the Manager can't handle it, so it's passed to the next handler in the chain, which is the Director.

7. When to use?

1. Use a Chain of Responsibility when you want to decouple a sender from its receivers.

2. When you have a set of objects that should be processed sequentially for a particular request.

3. When the set of handlers and their order are supposed to change dynamically.


Comments