Design Pattern articles in this series are listed below:
Design Patterns - An Overview
Design Patterns – Using the Strategy Pattern in C#
Design Patterns – Using the Singleton Pattern in C#
Design Patterns – Using the Adapter Pattern in C#
Design Patterns - Using the Facade Pattern in C#
Design Patterns - Using the Composite Pattern in C#
Design Patterns - Using the Decorator Pattern in C#
Design Patterns - Using the Abstract Factory Pattern in C#
Design Patterns – Using the Builder Pattern in C#
Design Patterns - Using the Observer Pattern in C# (Events and Delegates)
Design Patterns – Using the Chain of Responsibility Pattern in C#
Design Patterns – Using the State Pattern in C#
Design Patterns – Which to use When? Take the Quiz!
What is the Chain of Responsibility Pattern?
The Chain of Responsibility Pattern is a behavioral pattern that works with several handlers that has limitations on what requests they could handle. If the handler cannot process a request, it passes on the request to a successor handler. At the end of this chain of handlers, there can either be default or exceptional behavior. This pattern is encountered very frequently in real life. Consider a computer retailer that provides tech support to it’s customers. When a customer calls with a simple problem, it’s usually handled by Level 1 Support. For a serious computer issue such as a broken system, the Level 1 support cannot handle this issue and usually pass this on to a higher level of support or a manager.
How is the Chain of Responsibility Pattern implemented?
The different roles in the Chain of Responsibility Pattern are:
1. Abstract Handler Class: A base class for all concrete handlers
2. Concrete Handler Class(es): One or more concrete handler classes that implement the behavior for handling a request or passing on the request to it’s successor.
3. Client: A class that sends requests to handlers
Let’s implement the Chain of Responsibility Pattern using an example. Let’s assume we are creating an application for a casino company. The casino likes to attract more high rollers as it helps the company’s bottom line. As players step into the casino, the company analyzes the amount of money the player has spent in the past. Based on this spending amount, he/she is greeted by different casino employees who have the authority to provide the players with different goodies (free accommodation, free drinks and so on)
1. If a player’s spending amount <= $5,000, the player is greeted at the door by a casino employee
2. If a player’s spending amount > $5,000 but <= $25,000, the player is greeted at the door by a casino supervisor
3. If a player’s spending amount > $25,000 but <= $100,000, the player is greeted at the door by a casino manager
What if a player’s spending amount > $100,000? In the Chain of Responsibility Pattern, a decision needs to be taken for processing requests that are not handled by the last successor in the chain. Based on the situation, we can either throw an exception or handle it another way. In our casino example, we would want to welcome players whose spending amount exceeds $100,000, so we should not throw an exception. Rather, we decide that these cases are handled by the casino manager as well.
Let’s take a look at the Abstract Handler class
//Base Handler Class
public abstract class BasePlayerHandler
{
protected BasePlayerHandler _nextHandler;
public abstract void HandlePlayer(Player player);
public void SetupHandler(BasePlayerHandler nextHandler)
{
_nextHandler = nextHandler;
}
}
The HandlePlayer method’s implementation that is provided by the individual handlers defines how the handler handles the request. The SetupHandler method helps in setting up the hierarchy of the casino company (Employee –> Supervisor –> Manager)
The handler classes are implemented as shown below
//Concrete Handler 1 - Employee
public class Employee : BasePlayerHandler
{
public override void HandlePlayer(Player player)
{
if (player.SpendingAmount <= 5000)
Console.WriteLine(String.Format("{0} is being greeted by a Employee", player.Name));
else
_nextHandler.HandlePlayer(player);
}
}
//Concrete Handler 2 - Supervisor
public class Supervisor : BasePlayerHandler
{
public override void HandlePlayer(Player player)
{
if (player.SpendingAmount <= 25000)
Console.WriteLine(String.Format("{0} is being greeted by a Supervisor", player.Name));
else
_nextHandler.HandlePlayer(player);
}
}
//Concrete Handler 3 - Manager
public class Manager : BasePlayerHandler
{
public override void HandlePlayer(Player player)
{
if (player.SpendingAmount <= 100000)
Console.WriteLine(String.Format("{0} is being greeted by a Manager", player.Name));
else
Console.WriteLine(String.Format("{0} is being greeted by a Manager", player.Name)); //Handling exception cases
}
}
Each handler checks for the SpendingAmount and makes a determination if they can handle the request or pass it on to the next higher up in the hierarchy for processing. Since we decided to have the manager handle requests of players with spending amount > $100000, the else block in the if statement in the Manager class handles this request as well.
The Player class simply has the name and the spending amount properties.
public class Player
{
public string Name { get; set; }
public int SpendingAmount { get; set; }
}
Finally, let’s take a look at the Client. The Client performs two distinct tasks:
1. It creates the hierarchy of handlers
2. It creates the players and all requests are initially routed through the lowest role in the Casino’s hierarchy – The Casino Employee.
class Program
{
static void Main(string[] args)
{
//Build the casino company's hierarchy
BasePlayerHandler employee = new Employee();
BasePlayerHandler supervisor = new Supervisor();
BasePlayerHandler manager = new Manager();
employee.SetupHandler(supervisor);
supervisor.SetupHandler(manager);
//Create players
Player joe = new Player();
joe.Name = "Joe";
joe.SpendingAmount = 95000;
Player jane = new Player();
jane.Name = "Jane";
jane.SpendingAmount = 200;
Player bob = new Player();
bob.Name = "Bob";
bob.SpendingAmount = 20000;
//Create an exception case
Player dave = new Player();
dave.Name = "Dave - The High roller";
dave.SpendingAmount = 900000;
//Handle players
employee.HandlePlayer(joe);
employee.HandlePlayer(jane);
employee.HandlePlayer(bob);
employee.HandlePlayer(dave);
}
}
The output generated is:
Physical Model – Class Diagram
Conclusion
The role of the Chain of Responsibility Pattern is to process requests by several handlers that have some limitation on the nature of the request they can process. The handler makes a determination whether the request needs to be handled or to pass it to the next handler in the chain. For simplicity, though the example shows a single Manager, Employee and a Supervisor, this pattern can also be implemented with multiple handlers of the same type.
If you liked this article, check out other articles in the Design Pattern series. You can subscribe to the RSS feed and/or follow DotNetCube on Twitter.