Suppose we have a group of objects that together are meant to solve a problem. When one object can't solve a problem, we want the object to send the task to a different object in a given chain. This is what the Chain of Responsibility design pattern is used for.
In order to get this to work, we need a handler, which will be our Chain
interface. The various objects in the chain will all implement this Chain
interface.
Let's start with a simple example; an associate can purchase an asset for less than $100, a manager can purchase something for less than $500.
Our abstraction for the Purchaser
interface looks like this:
<?php interface Purchaser { public function setNextPurchaser(Purchaser $nextPurchaser): bool; public function buy($price): bool; }
Our first implementation is the Associate
class. Quite simply, we implement the setNextPurchaser
function so that it will set the nextPurchaser
class property to the next object in the chain.
When we call the buy
function, if the price is within range, the associate will purchase it. If not, the next purchaser in the chain will purchase it:
<?php class AssociatePurchaser implements Purchaser { public function setNextPurchaser(Purchaser $nextPurchaser): bool { $this->nextPurchaser = $nextPurchaser; return true; } public function buy($price): bool { if ($price < 100) { var_dump("Associate purchased"); return true; } else { if (isset($this->nextPurchaser)) { reurn $this->nextPurchaser->buy($price); } else { var_dump("Could not buy"); return false; } } } }
Our Manager
class is exactly the same; we just allow the manager to purchase assets which are under $500. In reality, when you apply this pattern you wouldn't just duplicate a class as your class would have different logic; this example is just an incredibly simple implementation.
Here's the code:
<?php class ManagerPurchaser implements Purchaser { public function setNextPurchaser(Purchaser $nextPurchaser): bool { $this->nextPurchaser = $nextPurchaser; return true; } public function buy($price): bool { if ($price < 500) { var_dump("Associate purchased"); return true; } else { if (isset($this->nextPurchaser)) { return $this->nextPurchaser->buy($price); } else { var_dump("Could not buy"); return false; } } } }
Let's run a basic purchase from an associate in our index.php
file.
Firstly, here's the code we put in our index.php
file:
<?php require_once('Purchaser.php'); require_once('AssociatePurchaser.php'); $associate = new AssociatePurchaser(); $associate->buy(50);
The output of all of this is as follows:
Next, let's test our Manager
class. We'll amend our purchase price in our index.php
file and also add our Manager
class to the chain.
Here's our amended index.php
:
<?php require_once('Purchaser.php'); require_once('AssociatePurchaser.php'); require_once('ManagerPurchaser.php'); $associate = new AssociatePurchaser(); $manager = new ManagerPurchaser(); $associate->setNextPurchaser($manager); $associate->buy(400);
This has the following output:
Let's see what happens if we alter the price such that the purchase will fail.
We change the final line on our index.php
file so the purchase price is now $600:
<?php require_once('Purchaser.php'); require_once('AssociatePurchaser.php'); require_once('ManagerPurchaser.php'); $associate = new AssociatePurchaser(); $manager = new ManagerPurchaser(); $associate->setNextPurchaser($manager); $associate->buy(600);
This has the following output:
We can now extend this script. Let's add DirectorPurchaser
and BoardPurchaser
so we can make purchases at a higher cost.
We'll create a DirectorPurchaser
who can buy under $10,000.
This class is as follows:
<?php class DirectorPurchaser implements Purchaser { public function setNextPurchaser(Purchaser $nextPurchaser): bool { $this->nextPurchaser = $nextPurchaser; return true; } public function buy($price): bool { if ($price < 10000) { var_dump("Director purchased"); return true; } else { if (isset($this->nextPurchaser)) { return $this->nextPurchaser->buy($price); } else { var_dump("Could not buy"); return false; } } } }
Let's do the same for a BoardPurchaser
class who can purchase below $100,000:
<?php class BoardPurchaser implements Purchaser { public function setNextPurchaser(Purchaser $nextPurchaser): bool { $this->nextPurchaser = $nextPurchaser; return true; } public function buy($price): bool { if ($price < 100000) { var_dump("Board purchased"); return true; } else { if (isset($this->nextPurchaser)) { return $this->nextPurchaser->buy($price); } else { var_dump("Could not buy"); return false; } } } }
Now we can update our index.php
script to require the new classes, instantiate them, and then bind everything together in a chain. Finally, we'll attempt to run a purchase by calling the first in the chain.
Here's the script:
<?php require_once('Purchaser.php'); require_once('AssociatePurchaser.php'); require_once('ManagerPurchaser.php'); require_once('DirectorPurchaser.php'); require_once('BoardPurchaser.php'); $associate = new AssociatePurchaser(); $manager = new ManagerPurchaser(); $director = new DirectorPurchaser(); $board = new BoardPurchaser(); $associate->setNextPurchaser($manager); $manager->setNextPurchaser($director); $director->setNextPurchaser($board); $associate->buy(11000);
Here's the output of this script:
This allows us to traverse a chain of objects to process data. This is particularly useful when dealing with tree data structures (for example, an XML tree). This can act in a launch-and-leave manner where we can lower the overhead of handling iterating through the chain.
Moreover, the chain is loosely coupled, data is passed through a chain until it is processed. Any object can be chained to any other object in any order.
18.191.60.249