Monday, July 8, 2024

Chain of Responsibility Pattern in C#

The Chain of Responsibility pattern is a behavioral design pattern that allows a group of objects to handle a request sequentially, each potentially handling the request or passing it on to the next object in the chain until the request is handled or reaches the end of the chain.

In simple words, we can say that the chain of responsibility design pattern creates a chain of receiver objects for a given request. In this design pattern, normally, each receiver contains a reference to the next receiver. If one receiver cannot handle the request, it passes the same request to the next receiver, and so on. In this case, one receiver can handle the request in the chain, or one or more receivers can handle the request.

Components of Chain of Resposibility Pattern

  1. Handler Interface (or Abstract Class): This defines a common interface for all handlers. It typically includes a method for handling requests and a reference to the next handler in the chain.
  2. Concrete Handlers: These are the actual handlers in the chain. Each handler implements the Handler interface and contains logic to handle requests. If it can't handle a request, it passes it to the next handler in the chain.
  3. Client: This initiates the request and sends it to the first handler in the chain.

Example in C#

Let's consider a scenario where we need to process purchase requests based on their amount. We'll implement the Chain of Responsibility pattern to handle these requests.

Creating Handler Interface(IPurchaseHandler.cs)
using ChainofResponsibilityPattern.Model;

namespace ChainofResponsibilityPattern.Handler
{
    /// <summary>
    /// Handler Interface
    /// </summary>
    public interface IPurchaseHandler
    {
        void HandleRequest(Purchase purchase);
    }
}
Creating Concrete Handlers
  1. Manager.cs
        using ChainofResponsibilityPattern.Model;
    
    namespace ChainofResponsibilityPattern.Handler
    {
        /// <summary>
        /// Concrete Handler 1
        /// </summary>
        public class Manager : IPurchaseHandler
        {
            private readonly IPurchaseHandler _nextHandler;
    
            public Manager(IPurchaseHandler nextHandler)
            {
                _nextHandler = nextHandler;
            }
    
            public void HandleRequest(Purchase purchase)
            {
                if (purchase.Amount <= 1000)
                {
                    Console.WriteLine($"Manager approved purchase of ${purchase.Amount}");
                }
                else
                {
                    _nextHandler.HandleRequest(purchase);
                }
            }
        }
    }
    
        
  2. Director.cs
        using ChainofResponsibilityPattern.Model;
    
    namespace ChainofResponsibilityPattern.Handler
    {
        /// <summary>
        /// Concrete Handler 2
        /// </summary>
        public class Director : IPurchaseHandler
        {
            private readonly IPurchaseHandler _nextHandler;
    
            public Director(IPurchaseHandler nextHandler)
            {
                _nextHandler = nextHandler;
            }
    
            public void HandleRequest(Purchase purchase)
            {
                if (purchase.Amount <= 5000)
                {
                    Console.WriteLine($"Director approved purchase of ${purchase.Amount}");
                }
                else
                {
                    _nextHandler.HandleRequest(purchase);
                }
            }
        }
    }
    
  3. CEO.cs
        using ChainofResponsibilityPattern.Model;
    
    namespace ChainofResponsibilityPattern.Handler
    {
        /// <summary>
        /// Concrete Handler 3
        /// </summary>
        public class CEO : IPurchaseHandler
        {
            public void HandleRequest(Purchase purchase)
            {
                Console.WriteLine($"CEO approved purchase of ${purchase.Amount}");
            }
        }
    }
        
Request Class(Purchase.cs)
namespace ChainofResponsibilityPattern.Model
{
    /// <summary>
    /// Request Class
    /// </summary>
    public class Purchase
    {
        public double Amount { get; }
        public Purchase(double amount)
        {
            Amount = amount;
        }
    }
}
Client(Program.cs)
using ChainofResponsibilityPattern.Handler;
using ChainofResponsibilityPattern.Model;
// Create chain of responsibility
var ceo = new CEO();
var director = new Director(ceo);
var manager = new Manager(director);

// Process purchase requests
var purchase1 = new Purchase(800);
var purchase2 = new Purchase(3500);
var purchase3 = new Purchase(10000);

manager.HandleRequest(purchase1);
manager.HandleRequest(purchase2);
manager.HandleRequest(purchase3);
Console.ReadLine();

Output

chain of responsibility pattern

In this example, we have three concrete handlers: Manager, Director, and CEO, each with different approval thresholds. Requests are processed by passing them through the chain starting from the Manager. If a handler can't approve the request, it passes it to the next handler in the chain until it's approved or rejected.

Conclusion

The Chain of Responsibility pattern promotes loose coupling by allowing multiple objects to handle a request without needing to know which object will handle it. It also provides flexibility in dynamically changing the chain and adding or removing handlers without affecting the client code.

The full source code is available here:

Happy coding!! 😊

No comments:

Post a Comment

^ Scroll to Top