The Chain of Responsibility Pattern

laurentiu.raducu

The Chain of Responsibility Pattern

5 minutes read

The Chain of Responsibility pattern is a behavioral design pattern that allows objects in a program to handle requests in a modular way. This pattern is often used to process input from a user or to handle events in a program. The Chain of Responsibility pattern is particularly useful when there are multiple objects that can handle a request, and the program does not need to know which object will ultimately handle the request.

In C#, the Chain of Responsibility pattern can be implemented using a linked list of objects that each have a reference to the next object in the chain. Each object in the chain has a method that can handle the request, and if the object is not able to handle the request, it passes the request to the next object in the chain.

To implement the Chain of Responsibility pattern in C#, you will need to define an interface that defines the method for handling the request. This interface should have a single method, such as HandleRequest() or Process() that takes the request as an input.

interface IHandler
{
    void HandleRequest(string request);
}

Next, you will need to create a concrete class that implements the IHandler interface and defines the logic for handling the request. This class should have a reference to the next object in the chain and should call the HandleRequest() method on the next object if it is not able to handle the request.

class ConcreteHandlerA : IHandler
{
    private IHandler _nextHandler;

    public void SetNextHandler(IHandler nextHandler)
    {
        _nextHandler = nextHandler;
    }

    public void HandleRequest(string request)
    {
        if (request == "requestA")
        {
            // Handle the request
            Console.WriteLine("ConcreteHandlerA handled the request.");
        }
        else if (_nextHandler != null)
        {
            _nextHandler.HandleRequest(request);
        }
    }
}

You can also create multiple ConcreteHandlers, each one of them will handle different request:

class ConcreteHandlerB : IHandler
{
    private IHandler _nextHandler;

    public void SetNextHandler(IHandler nextHandler)
    {
        _nextHandler = nextHandler;
    }

    public void HandleRequest(string request)
    {
        if (request == "requestB")
        {
            // Handle the request
            Console.WriteLine("ConcreteHandlerB handled the request.");
        }
        else if (_nextHandler != null)
        {
            _nextHandler.HandleRequest(request);
        }
    }
}

In order to use the Chain of Responsibility pattern in a program, you will need to create an instance of each ConcreteHandler and link them together in a chain. This can be done by calling the SetNextHandler() method on each object to set the next object in the chain.

IHandler handlerA = new ConcreteHandlerA();
IHandler handlerB = new ConcreteHandlerB();
handlerA.SetNextHandler(handlerB);

handlerA.HandleRequest("requestA");
handlerA.HandleRequest("requestB");

In this example, the first call to HandleRequest(“requestA”) will be handled by ConcreteHandlerA and the second call to HandleRequest(“requestB”) will be handled by ConcreteHandlerB.

The Chain of Responsibility pattern is a

powerful pattern that allows for a more modular and maintainable codebase. It allows for a clear separation of concerns, as each object in the chain is responsible for handling a specific type of request. Additionally, it makes it easy to add new objects to the chain, as they can be added without affecting the existing objects in the chain.

One potential downside of the Chain of Responsibility pattern is that it can make the code more complex, as each object in the chain needs to have a reference to the next object in the chain. However, this complexity can be mitigated by using a builder pattern to construct the chain of objects.

Another potential downside is that if the program does not have a clear way to determine which object should handle a request, the request may go unhandled. To prevent this, it is important to have a fallback mechanism in place, such as a default handler that can handle any request that goes unhandled by the objects in the chain.

If you are interested to learn more about adapter pattern, or want to see a demo of this pattern in Java code, I have published this course on Udemy. You can see there all the known design patterns described in detail with relevant, real-world examples.