Chain of Responsibility

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:

Chain of Responsibility

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:

Chain of Responsibility

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:

Chain of Responsibility

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:

Chain of Responsibility

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.60.249