Development Blog

Collection of development articles I've put together...
Return to articles

Chain Of Responsibility Pattern

To build a chain of objects to handle a call in sequential order. If one object cannot handle a call, it delegates the call to the next in the chain and so forth.
1
Likes
2 years ago
Design Patterns: Behavioural
Purpose

To build a chain of objects to handle a call in sequential order. If one object cannot handle a call, it delegates the call to the next in the chain and so forth.

Examples
  • Logging framework, where each chain element decides autonomously what to do with a log message
  • A Spam filter
  • Caching: first object is an instance of e.g. a Memcached Interface, if that "misses" it delegates the call to the database interface
UML


Code

Handler


/**
* The Handler interface declares a method for building the chain of handlers.
* It also declares a method for executing a request.
*/
interface Handler
{
    public function setNext(Handler $handler): Handler;
    public function handle(string $request): ?string;
}

Abstract Handler


/**
* The default chaining behavior can be implemented inside a base handler class.
*/
abstract class AbstractHandler implements Handler
{
   /**
    * @var Handler
    */
    private $nextHandler;

    public function setNext(Handler $handler): Handler
    {
        $this->nextHandler = $handler;
        // Returning a handler from here will let us link handlers in a
        // convenient way like this:
        // $stu->setNext($cheryl)->setNext($millie)->setNext($harry);
        return $handler;
    }

    public function handle(string $request): ?string 
    {
      if ($this->nextHandler) {
          return $this->nextHandler->handle($request);
      }

        return null;
    }
}

Concrete Sub Classes


/**
* The client code is usually suited to work with a single handler. In most
* cases, it is not even aware that the handler is part of a chain.
*/
function clientCode(Handler $handler)
{
    foreach (["Nandos", "Mixed Grill", "Cup of coffee", "Burger King", "McDonalds", "Prawns"] as $food) {
        echo "Client: Who wants a " . $food . "?\n";
        $result = $handler->handle($food);
        if ($result) {
             echo "  " . $result;
        } else {
             echo "  " . $food . " was left alone.\n";
        }
    }
}

/**
* Build the chain first
*/
$stu = new StuHandler();
$cheryl = new CherylHandler();
$millie = new MillieHandler();
$harry = new HarryHandler();
$stu->setNext($cheryl)->setNext($millie)->setNext($harry);

/**
* The client should be able to send a request to any handler, not just the
* first one in the chain.
*/
echo "Chain: Stu > Cheryl > Millie > Harry\n\n";
clientCode($stu);
echo "\n";

echo "Subchain: Millie > Harry\n\n";
clientCode($millie);

Test output


Chain: Stu > Cheryl > Millie > Harry

Client: Who wants a Nandos?
Millie: I'll eat the Nandos.
Client: Who wants a Mixed Grill?
Stu: I'll eat the Mixed Grill.
Client: Who wants a Cup of coffee?
Cup of coffee was left alone.
Client: Who wants a Burger King?
Burger King was left alone.
Client: Who wants a McDonalds?
Harry: I'll eat the McDonalds.
Client: Who wants a Prawns?
Cheryl: I'll eat the Prawns.

Subchain: Millie > Harry

Client: Who wants a Nandos?
Millie: I'll eat the Nandos.
Client: Who wants a Mixed Grill?
Mixed Grill was left alone.
Client: Who wants a Cup of coffee?
Cup of coffee was left alone.
Client: Who wants a Burger King?
Burger King was left alone.
Client: Who wants a McDonalds?
Harry: I'll eat the McDonalds.
Client: Who wants a Prawns?
Prawns was left alone.