Introduction to Chain of Responsibility

Nicola Pietroluongo
Nicola Pietroluongo
Share

In this article, we’ll explain and demonstrate the Chain of Responsibility pattern.

Definition

The Chain of Responsibility is a behavioral design pattern that processes a request through a series of processor (handlers/receivers) objects. The request is sent from one handler object to another and processed by one (pure implementation) or all of the handlers. All the handlers are part of the chain.

Two simple examples containing a chain logic are:

  • a person using an ATM to get cash, (enter pin, amount, receipt);
  • a help desk call (options list, press dial buttons, follow the steps).

Participants

The pattern in the short version includes:

  • The Handler: defines an interface for handling requests. It can be an abstract class, which optionally implements default methods and the way to set a successor in the chain.
  • Many concrete handler objects: process the request and optionally provide access to successors;

CoR can also include:
– a Client object to perform the request and set up the chain;
– a Request object;
– a Response object;
– other design patterns.

The CoR design pattern is not one that is used often, but its core logic makes it useful in several cases.

CoR is useful when:

  • the handler must be determined automatically (e.g., a logging system);
  • the handler cannot be known in advance (e.g., handling exceptions);
  • the request must pass through a specific chain’s priority (e.g., event propagation, command propagation) or the order of the chain must be dynamic.

Basic usage

CoR is often applied in conjunction with Composite pattern, so it’s easy to treat all the handlers in the same way and dispatch the request to the chain’s successors.
Below is a basic PHP example:

<?php
abstract class BasicHandler
{
    /**
     * @var Handler
     */
    private $successor = null;

    /**
     * Sets a successor handler.
     * 
     * @param Handler $handler
     */
    public function setSuccessor(Handler $handler)
    {
        $this->successor = $handler;
    }

    /**
     * Handles the request and/or redirect the request
     * to the successor.
     *
     * @param mixed $request
     *
     * @return mixed
     */
    abstract public function handle($request);

}

Below is an example (not fully implemented) continuing with the code above:

class FirstHandler extends BasicHandler
{
    public function handle($request)
    {
        //provide a response, call the next successor
    }
}

// .. code for SecondHandler and ThirdHandler classes ..

$firstHandler = new FirstHandler();
$secondHandler = new SecondHandler();
$thirdHandler = new ThirdHandler();

$firstHandler->setSuccessor($secondHandler);
$secondHandler->setSuccessor($thirdHandler);

$result = $firstHandler->handle($request);

Advanced usage

The power behind this pattern is the flexibility and the open logic in the chain organization.
This flexibility becomes clearer with some examples.

From the BasicHandler code, it’s possible to automate the handle method (with some constraints) moving its scope from the children to the parent abstract class.

While the example below doesn’t solve all the CoR issues, it shows how the pattern can be easily restructured and adapted to different logic.

<?php
abstract class AdvancedHandler
{
    /**
     * @var Handler
     */
    private $successor = null;

    /**
     * Sets a successor handler,
     * in case the class is not able to satisfy the request.
     *
     * @param Handler $handler
     */
    final public function setSuccessor(Handler $handler)
    {
        if ($this->successor === null) {
            $this->successor = $handler;
        } else {
            $this->successor->setSuccessor($handler);
        }
    }

    /**
     * Handles the request or redirect the request
     * to the successor, if the process response is null.
     *
     * @param string|array $data
     *
     * @return string
     */
    final public function handle($request)
    {
        $response = $this->process($request);
        if (($response === null) && ($this->successor !== null)) {
            $response = $this->successor->handle($request);
        }

        return $response;
    }

    /**
     * Processes the request.
     * This is the only method a child can implements, 
     * with the constraint to return null to dispatch the request to next successor.
     * 
     * @param $request
     *
     * @return null|mixed
     */
    abstract protected function process($request);
}
class FirstHandler extends AdvancedHandler
{
    public function process($request)
    {
        //do something
    }
}

// .. code for SecondHandler and ThirdHandler classes ..
$firstHandler = new FirstHandler();
$secondHandler = new SecondHandler();
$thirdHandler = new ThirdHandler();

//the code below sets all successors through the first handler
$firstHandler->setSuccessor($secondHandler);
$firstHandler->setSuccessor($thirdHandler);

$result = $firstHandler->handle($request);

The example above minimizes the methods inside the Concrete Handler (increasing the cohesion) despite the constraint on a not null response.

If it’s necessary to go to the end of the chain for each request, it’s easy to refactor the handle method returning the response when the successor is null, because the last chain’s handler doesn’t have a successor. It is also possible to use a more structured Response object.

There are several modifications to the CoR – for example, combining this pattern with others, like the Factory or Decorator pattern to obtain an incremental construction/change of the Response.

Real world examples include the making of a car (chassis, interior, exterior, painting, wheels, etc.), or the creation of a web page (header, body, content, and footer).

I encourage you to explore other pattern modifications.

Next, we’ll look at some suggestions on common CoR issues like configuring the priority and speeding up the chain.

Configuring the chain

Extracting the logic for configuring the chain’s handlers helps keep the code clean and allows for easy changes.

Using a configuration object with Dependency Injection we can make the configuration global (e.g. via a yaml file).

class Client
{
    private $firstHandler;
    
    public function setChainOrder($handlersOrder)	
    {
        //code for setup the chain
    }
    public function process($request)
    {
        $response = $this->firstHandler->handle($request);
        
        return $response; 
    }
}
$client = new Client();
$client->setChainOrder($myHanldersOrder);
$client->process($request);

Dependency Injection can allow for more flexibility. For example, the chain can be configured by each Request object, as in the example below, with a little code modification.

$client = new Client();
$handlersOrder = $request->getHandlersOrder();
$client->setChainOrder($handlersOrder);
$client->process($request);

Speeding up the chain

If the CoR is stressed by several requests and the handlers are also complex – maybe they create other classes to process the request – a Service Container could be useful.

A Service Container (or dependency injection container) that manages the instantiation of objects without creating them each time helps improve memory and performance.

If CoR receives the same type of request often, a caching system is useful for improving speed.
One possible solution is to use an implementation of the Flyweight Pattern to store responses and provide the stored response (if it exists) on identical requests, without having to process the chain again.

In this example, I have added a simple cache to the AdvancedHandler class.

abstract class AdvancedHandler
{
//code ...
    final public function handle($request)
    {
        $response = $this->cache->getResponse($request);
        if ($response === null) {
            $response = $this->handleRequestWithChain($request);    
        }
        
        return $response;
    }   

    final public function handleRequestWithChain($request)
    {
        $response = $this->process($request);
        if (($response === null) && ($this->successor !== null)) {
            $response = $this->successor->handleRequestWithChain($request);
        }
        $this->cache->setResponse($response);

        return $response;
    }
//code ...    
}

Final thoughts

The Chain of Responsibility can be a very powerful pattern. It is also possible to implement a chain of chains, making a multidimensional structure.

CoR promotes loose coupling: every handler could be changed (or removed) without a big impact on the whole structure, but its flexibility, with a wrong design, can cause problems on all the chain’s objects involved.

I advise you to make a thorough analysis of the problem you are trying to solve before implementing the CoR pattern, and pay attention to the definition of the handler (abstract class or interface), the request, the response and their interactions.

Frequently Asked Questions (FAQs) about Chain of Responsibility Design Pattern

What is the main advantage of using the Chain of Responsibility design pattern?

The primary advantage of using the Chain of Responsibility design pattern is that it decouples the sender and receiver of a request. This means that the sender doesn’t need to know the details of who handles the request, or how it’s handled. The request can be passed along a chain of objects until it is processed. This promotes loose coupling and flexibility in code structure, making it easier to add, remove, or reorder the objects in the chain without affecting the sender.

How does the Chain of Responsibility pattern differ from other behavioral design patterns?

Unlike other behavioral design patterns, the Chain of Responsibility pattern creates a linear sequence or a chain of receiver objects for a request. Each receiver in the chain either handles the request or passes it to the next receiver. This differs from patterns like Observer or Strategy, where a single object is responsible for maintaining and notifying observers, or where an algorithm is selected and applied within a context, respectively.

Can the Chain of Responsibility pattern be used with other design patterns?

Yes, the Chain of Responsibility pattern can be combined with other design patterns for more complex scenarios. For instance, it can be used with the Composite pattern, which allows you to treat a group of objects in the same way as a single instance of an object. This can be useful when you want to create a tree-like structure and have requests flow through it.

What are some common use cases for the Chain of Responsibility pattern?

The Chain of Responsibility pattern is commonly used in software systems where multiple objects may handle a request, but the exact handler isn’t known at compile time. Examples include event handling systems in GUI libraries, where events can be handled by any number of elements in a nested structure, or middleware in web server frameworks, where requests can be processed by a series of middleware functions in a chain.

How does error handling work in the Chain of Responsibility pattern?

In the Chain of Responsibility pattern, if an object in the chain can’t handle the request, it passes the request to the next object. If no object in the chain can handle the request, it can either be handled by a default handler or result in an error. The pattern doesn’t define any specific error handling mechanism, so it’s up to the developer to decide how to handle such scenarios.

Is the Chain of Responsibility pattern applicable only to object-oriented programming?

While the Chain of Responsibility pattern is typically associated with object-oriented programming due to its use of objects and their relationships, the core concept can be applied in other programming paradigms as well. The key idea is to have a series of handlers processing requests, which can be implemented in various ways depending on the language and paradigm.

How does the Chain of Responsibility pattern affect performance?

The Chain of Responsibility pattern can potentially impact performance, as a request might need to be passed through several handlers before it is processed. However, this impact is usually negligible unless the chain is extremely long or the processing within each handler is complex. The pattern’s benefits in terms of code flexibility and maintainability often outweigh any minor performance concerns.

Can the Chain of Responsibility pattern lead to infinite loops?

If improperly implemented, the Chain of Responsibility pattern can lead to infinite loops. This can happen if a request is passed along the chain without any handler being able to process it, and the chain is structured in such a way that the request cycles back to the start. To avoid this, it’s important to have a termination condition or a default handler at the end of the chain.

How does the Chain of Responsibility pattern handle multiple requests?

The Chain of Responsibility pattern can handle multiple requests by passing each request along the chain until it is processed. Each request is handled independently, so multiple requests can be in different stages of processing along the chain at the same time. This makes the pattern suitable for scenarios where multiple, potentially different, requests need to be handled concurrently.

Can the order of handlers in the Chain of Responsibility pattern be changed dynamically?

Yes, the order of handlers in the Chain of Responsibility pattern can be changed dynamically. This is one of the pattern’s strengths, as it provides flexibility in how requests are handled. By changing the order of handlers, you can prioritize certain types of requests, add new handlers, or remove existing ones without affecting the rest of the chain.