Back to: Design Patterns in C# With Real-Time Examples
Chain of Responsibility Design Pattern in C#
In this article, I will discuss the Chain of Responsibility Design Pattern in C# with Examples. Please read our previous article discussing the Observer Design Pattern in C# with one Real-Time Example. The Chain of Responsibility Design Pattern falls under the category of Behavioral Design Pattern. As part of this article, we will discuss the following pointers.
- What is the Chain of Responsibility Design Pattern?
- Understanding the Chain of Responsibility Design Pattern.
- Real-time Example of Chain of Responsibility Design Pattern.
- Implementation of Chain of Responsibility Design Pattern in C#.
- Advantages of Chain of Responsibility Design Pattern.
- When to Use Chain of Responsibility Design Pattern in C#?
What is the Chain of Responsibility Design Pattern?
According to the Gang of Four Definitions, the Chain of Responsibility Design Pattern states, “Avoid coupling the sender of a request to its receiver by giving more than one receiver object a chance to handle the request. Chain the receiving objects and pass the request along until an object handles it”.
The Chain of Responsibility Design Pattern is a Behavioral Design Pattern that allows passing requests along a chain of handlers. Instead of sending a request directly to a specific receiver, a chain of potential receivers is formed, and each handler either processes the request or passes it to the next handler in the chain. This pattern allows multiple objects to handle the request without coupling the sender class to the concrete classes of the receivers.
In simple words, we can say that the chain of responsibility design pattern creates a chain of receiver objects for a given request. In this design pattern, normally, each receiver contains a reference to the next receiver. If one receiver cannot handle the request, it passes the same request to the next receiver, and so on. In this case, one receiver can handle the request in the chain, or one or more receivers can handle the request.
Understanding the Chain of Responsibility Design Pattern:
Please look at the following diagram to understand the Chain of Responsibility Design Pattern. As shown in the diagram below, on the left side, we have a client. On the right side, we have multiple receivers chained together (i.e., receiver 1 has a reference to receiver 2, receiver 2 has a reference to receiver 3, and so on).
Here, the client sends the request to the Chain of Receivers. In the chain of receivers, the first receiver is Receiver 1. So, the request will come to Receiver 1, and Receiver 1 will check whether it can handle the request. If it can handle the request, then it will handle the request and then check whether further processing is needed for the request or not. If further processing is needed, it will send the request to the next receiver (i.e., Receiver 2) in the chain of receivers. If the request does not need further processing, it will not pass it to the next receiver.
Receiver 2 will then check whether it can handle the request or not. If it can handle the request, then it will handle the request and then check whether further processing is needed or not. If further processing is needed, it will send the request to the next receiver (i.e., Receiver 3). It will not pass the request to the next receiver if it doesn’t need further processing. In this way, the chain of responsibility works.
Real-Time Example to Understand Chain of Responsibility Design Pattern:
Let us understand the Chain of Responsibility Design Pattern with one Real-Time Example. Please have a look at the following image. As shown in the image below, we have an ATM machine and four handlers. The TwoThousandhandler will give 2000 rupees. Similarly, the FiveHundredHandler will give 500 hundred rupees and the same for 200 and 100 handlers.
Now, Anurag wants to withdraw 4600 rupees from the ATM machine. So, what the ATM machine will do is send the request to the first handler, i.e., the TwoThousandHandler, and the TwoThousandHandler will check the amount and give two 2000 rupees notes, and then the remaining amount is 600 rupees. So, the TwoThousandHandler will send the request to the FiveHundredHandler. Then, the FiveHundredHandler will check the remaining amount and give one 500 rupee note, and the remaining amount is 100 rupees. So, the FiveHundredHandler will forward the request to the next handler, i.e., TwoHundredHandler, and the TwoHundredHandler will check the remaining amount, which is 100. So, it can’t handle the request and forwards it to the next handler, HundredHandler. The HundredHandler checks the remaining amount, which is 100, and will give one 100 rupees note. This way, it will handle the request and provide Anurag with 4600 (2 * 2000, 1*500, and 1*100). This is one of the best examples of one or more receivers in the chain handling the request.
Implementation of Chain of Responsibility Design Pattern in C#:
Let us implement the above-discussed example step by step using the Chain of Responsibility Design Pattern in C#.
Step1: Creating Abstract Handler
Please create a class file named Handler.cs and copy and paste the following code. Here, you can see that we created one variable of type Handler (i.e., NextHandler), and this variable will hold the reference of the Next Handler. We have initialized this variable through the class constructor. What is the next handler? If this is not clear now, don’t worry; you will understand the need for this variable after a while. The concrete handler classes will implement this abstract DispatchNote() method.
namespace ChainOfResponsibilityDesignPattern { // Handler Abstract Class // The Default Chaining Behavior can be implemented inside the abstract handler class. public abstract class Handler { //The NextHandler will hold the reference of the next handler public Handler NextHandler; //Initializing NextHandler reference using the class constructor public void SetNextHandler(Handler NextHandler) { this.NextHandler = NextHandler; } //The following Method needs to be implemented by the Child handler Classes //The following method is going to handle a request. public abstract void DispatchNote(long requestedAmount); } }
Step2: Creating Concrete Handlers
Here, we will create four handlers (TwoThousandHandler, FiveHundredHandler, TwoHundredHandler, and HundredHandler) to handle the respective currency. Create a class file named TwoThousandHandler.cs and copy and paste the following code. This class inherits from the Handler abstract class and implements the abstract DispatchNote method.
using System; namespace ChainOfResponsibilityDesignPattern { //Concrete Handler 1 //The following class implement the Handler abstract class and //Provide Implementation for DispatchNote abstract method public class TwoThousandHandler : Handler { public override void DispatchNote(long requestedAmount) { //First Check the Number of 2000 Notes To Be Dispatched long numberofNotesToBeDispatched = requestedAmount / 2000; if (numberofNotesToBeDispatched > 0) { if (numberofNotesToBeDispatched > 1) { Console.WriteLine(numberofNotesToBeDispatched + " Two Thousand notes are dispatched by TwoThousandHandler"); } else { Console.WriteLine(numberofNotesToBeDispatched + " Two Thousand note is dispatched by TwoThousandHandler"); } } //Then check the Pending amount long pendingAmountToBeProcessed = requestedAmount % 2000; //If the Pending amount is greater than 0, then call the next handler to handle the request if (pendingAmountToBeProcessed > 0) { //For TwoThousandHandler, the next handler is FiveHundredHandler NextHandler.DispatchNote(pendingAmountToBeProcessed); } } } }
First, as part of the DispatchNote() method, we calculate the number of 2000 notes to be dispatched. If the amount is greater than 1, then we print how many 2000 notes are in the console window. Then we check the remaining amount, and if the remaining is greater than 0, we call the DispatchNote() method of the next handler (NextHandler), i.e., the FiveHundredHandler.
FiveHundredHandler:
Create a class file named FiveHundredHandler.cs and copy and paste the following code.
using System; namespace ChainOfResponsibilityDesignPattern { //Concrete Handler 2 //The following class implement the Handler abstract class and //Provide Implementation for DispatchNote abstract method public class FiveHundredHandler : Handler { public override void DispatchNote(long requestedAmount) { //First Check the Number of 500 Notes To Be Dispatched long numberofNotesToBeDispatched = requestedAmount / 500; if (numberofNotesToBeDispatched > 0) { if (numberofNotesToBeDispatched > 1) { Console.WriteLine(numberofNotesToBeDispatched + " Five Hundred notes are dispatched by FiveHundredHandler"); } else { Console.WriteLine(numberofNotesToBeDispatched + " Five Hundred note is dispatched by FiveHundredHandler"); } } //Then check the Pending amount long pendingAmountToBeProcessed = requestedAmount % 500; //If Pending amount is greater than 0, then call the next handler to handle the request if (pendingAmountToBeProcessed > 0) { //For FiveHundredHandler, the next handler is TwoHundredHandler NextHandler.DispatchNote(pendingAmountToBeProcessed); } } } }
Here, we also do the same thing as in the previous handler. Similarly, we need to implement TwoHundredHandler and HundredHandler.
TwoHundredHandler:
Create a class file named TwoHundredHandler.cs and copy and paste the following code.
using System; namespace ChainOfResponsibilityDesignPattern { //Concrete Handler 3 //The following class implement the Handler abstract class and //Provide Implementation for DispatchNote abstract method public class TwoHundredHandler : Handler { public override void DispatchNote(long requestedAmount) { //First Check the Number of 200 Notes To Be Dispatched long numberofNotesToBeDispatched = requestedAmount / 200; if (numberofNotesToBeDispatched > 0) { if (numberofNotesToBeDispatched > 1) { Console.WriteLine(numberofNotesToBeDispatched + " Two Hundred notes are dispatched by TwoHundredHandler"); } else { Console.WriteLine(numberofNotesToBeDispatched + " Two Hundred note is dispatched by TwoHundredHandler"); } } //Then check the Pending amount long pendingAmountToBeProcessed = requestedAmount % 200; //If the Pending amount is greater than 0, then call the next handler to handle the request if (pendingAmountToBeProcessed > 0) { //For TwoHundredHandler, the next handler is HundredHandler NextHandler.DispatchNote(pendingAmountToBeProcessed); } } } }
HundredHandler:
Create a class file with the name HundredHandler.cs and copy and paste the following code.
using System; namespace ChainOfResponsibilityDesignPattern { //Concrete Handler 4 //The following class implement the Handler abstract class and //Provide Implementation for DispatchNote abstract method public class HundredHandler : Handler { public override void DispatchNote(long requestedAmount) { //First Check the Number of 100 Notes To Be Dispatched long numberofNotesToBeDispatched = requestedAmount / 100; if (numberofNotesToBeDispatched > 0) { if (numberofNotesToBeDispatched > 1) { Console.WriteLine(numberofNotesToBeDispatched + " Hundred notes are dispatched by HundredHandler"); } else { Console.WriteLine(numberofNotesToBeDispatched + " Hundred note is dispatched by HundredHandler"); } } //No Need to Check the Next Handler } } }
Step3: Chaining the Handlers
In our example, ATM has managed the sequence in which all the handlers will be chained together. This class is used for internal processing and holding request details. The client is going to use this class. So, create a class file named ATM.cs and copy and paste the following code. In the following code, we are creating four instance variables of our four concrete handlers, and through the constructor, we prepare the sequence of the chain of handlers. We also provide one method (i.e., Withdraw()), which the client will consume.
using System; namespace ChainOfResponsibilityDesignPattern { // This class managed the sequence in which all the handlers are going to be chained together // This class initiates the request to a ConcreteHandler object on the chain public class ATM { private TwoThousandHandler twoThousandHandler = new TwoThousandHandler(); private FiveHundredHandler fiveHundredHandler = new FiveHundredHandler(); private TwoHundredHandler twoHundredHandler = new TwoHundredHandler(); private HundredHandler hundredHandler = new HundredHandler(); public ATM() { // Prepare the chain of Handlers // Here, we need to set the next handler of each handler twoThousandHandler.SetNextHandler(fiveHundredHandler); fiveHundredHandler.SetNextHandler(twoHundredHandler); twoHundredHandler.SetNextHandler(hundredHandler); } //The following method handle the request and passes it to the first handler in the chain of responsibility. public void Withdraw(long requestedAmount) { //First check whether the amount is Divisible by 100 or not if(requestedAmount % 100 == 0) { twoThousandHandler.DispatchNote(requestedAmount); } else { Console.WriteLine($"You Enter Invalid Amount: {requestedAmount}"); } } } }
Step4: Client
The Main method of the Program class is going to be the Client. So, modify the Main method of the Program class as shown below. As you can see, here we are creating an instance of ATM class and calling the Withdraw method by passing the amount.
using System; namespace ChainOfResponsibilityDesignPattern { class Program { static void Main(string[] args) { ATM atm = new ATM(); Console.WriteLine("Requested Amount 4600"); atm.Withdraw(4600); Console.WriteLine("\nRequested Amount 1900"); atm.Withdraw(1900); Console.WriteLine("\nRequested Amount 600"); atm.Withdraw(600); Console.WriteLine("\nRequested Amount 750"); atm.Withdraw(750); Console.Read(); } } }
Output:
Chain of Responsibility Design Pattern UML or Class Diagram:
Let us understand the Class Diagram or UML Diagram of the Chain of Responsibility Design Pattern and understand the different components involved. Please have a look at the following image.
As you can see in the above image, the Chain of Responsibility Design Pattern consists of three components. They are as follows:
- Handler: This will be an abstract class that defines how the request will be handled. It contains a member that holds the next handler in the chain and an associated method to set this next handler. It also has an abstract method that concrete handler classes will implement to handle the incoming request, and if required, it will pass the request to the next handler object in the pipeline. In our example, it is the Handler abstract class. The member is NextHandler, the associated method is SetNextHandler, and the abstract method is DispatchNote.
- ConcreteHandlerA and ConcreteHandlerB: These will be concrete classes inherited from the Handler abstract class and provide implementations for the abstract method. This method has the logic to handle the request. If required, then it will forward the request to the next handler associated with the pipeline. In our example, it is the DispatchNote method of the TwoThousandHandler, FiveHundredHandler, TwoHundredHandler, and HundredHandler classes.
- Client: This class generates the request and passes it to the first handler in the chain of responsibility. In our example, we have simplified this using the ATM class, and the client will call the Withdraw method of the ATM class.
Understanding One Receiver in the Chain to Handles the Request:
Let us understand the Chain of Responsibility Design Pattern with one Receiver Handling the Request with one Real-Time Example. Please look at the following diagram, which shows the reporting hierarchy in a software organization. As you can see, the Developer is reporting to Team Leader. The Team Leader reports to the Project Leader, and the Project Leader reports to HR. Again, the Team Leader can approve leave for a maximum of 10 Days. The Project Leader can approve leave for a maximum of 20 Days, and the HR can approve leave for 30 Days.
Suppose a developer wants to take a leave of 25 days. So, what the developer will do is he will send a request for 25 days’ leave to the Team Leader. The Team Leader will check whether he can approve the leave or not. As the leave is for 25 days and he can only approve up to 10 days, he will forward the request to the Project Leader.
The Project Leader will check whether he can approve the leave or not. As the leave is for 25 days, the Project Leader will not approve the leave as his capacity is up to 20 days. So, what the project leader will do is he will pass the request to HR, and HR will check and approve the leave.
On the other hand, if the developer is asking for 5 days’ leave, then this can be handled by the Team Leader, and once the Team Leader handles this, he will not forward this request to the Project Leader.
So, the point that you need to remember is once any handler handles the request, it should not forward that request to the next handler. Let’s see the step-by-step procedure to implement the above example using the Chain of Responsibility Design Pattern in C#.
Step1: Creating Abstract Handler
Create a class file named EmployeeLeaveHandler.cs and copy and paste the following code. Here, we created one variable, i.e., Supervisor, to hold the next handler instance, and we initialize this variable through the constructor. We also declare one abstract method, i.e., ApplyLeave, which the Concrete handlers will implement. The following example code is self-explained, so please go through the comment lines for a better understanding.
namespace ChainOfResponsibilityDesignPatternExample { // Handler Abstract Class // The Default Chaining Behavior can be implemented inside the abstract handler class. public abstract class EmployeeLeaveHandler { //The Supervisor will hold the reference of the Next Handler protected EmployeeLeaveHandler Supervisor; //Initializing Supervisor reference using the class constructor public void SetNextSupervisor(EmployeeLeaveHandler Supervisor) { this.Supervisor = Supervisor; } //The following Method needs to be implemented by the Child handler Classes //The following method is going to handle the request. public abstract void ApplyLeave(string EmployeeName, int NumberOfDaysLeave); } }
Step2: Creating Concrete Handlers
Now, we are going to create concrete handlers. These will be classes, and they should implement the EmployeeLeaveHandler abstract class and need to provide implementations for the ApplyLeave method.
Team Leader:
First, create a class file named TeamLeader.cs and copy and paste the following code. As you can see here, first, we created one variable, i.e., MAX_LEAVES_CAN_APPROVE, to hold the maximum leave value the Team Leader can approve. This class implements the EmployeeLeaveHandler abstract class and provides the implementation for the ApplyLeave() method. As part of the ApplyLeave method, we check whether the Team Leader can approve the number of leaves the employee applies. If yes, then it is called the ApproveLeave method. If not, then pass the request to the Project Leader.
using System; namespace ChainOfResponsibilityDesignPatternExample { //Concrete Handler 1 //The following class implement the EmployeeLeaveHandler abstract class and //Provide Implementation for ApplyLeave abstract method public class TeamLeader : EmployeeLeaveHandler { // TeamLeader can only approve up to 10 days of leave private readonly int MAX_LEAVES_CAN_APPROVE = 10; public override void ApplyLeave(string EmployeeName, int NumberOfDaysLeave) { //Check if TeamLeader can process this request if (NumberOfDaysLeave <= MAX_LEAVES_CAN_APPROVE) { //ApproveLeave(EmployeeName, NumberOfDaysLeave); Console.WriteLine($"TeamLeader Approved {NumberOfDaysLeave} Days Leave for the Employee {EmployeeName}"); } //If TeamLeader can't process the LeaveRequest then pass it on to the supervisor(Project Leader) // so that he can process else { Supervisor.ApplyLeave(EmployeeName, NumberOfDaysLeave); } } } }
Project Leader:
Create a class file named ProjectLeader.cs and copy and paste the following code. As you can see, the MAX_LEAVES_CAN_APPROVE variable holds the maximum leave value the Project Leader can approve. As part of the ApplyLeave method, we check whether the Project Leader can approve the number of leaves the employee applies. If yes, then it is called the ApproveLeave method. If not, then pass the request to HR.
using System; namespace ChainOfResponsibilityDesignPatternExample { //Concrete Handler 2 //The following class implement the EmployeeLeaveHandler abstract class and //Provide Implementation for ApplyLeave abstract method class ProjectLeader : EmployeeLeaveHandler { // ProjectLeader can only approve up to 20 days of leave private readonly int MAX_LEAVES_CAN_APPROVE = 20; public override void ApplyLeave(string EmployeeName, int NumberOfDaysLeave) { //Check if ProjectLeader can process this request if (NumberOfDaysLeave <= MAX_LEAVES_CAN_APPROVE) { Console.WriteLine($"ProjectLeader Approved {NumberOfDaysLeave} Days Leave for the Employee {EmployeeName}"); } //If ProjectLeader can't process the LeaveRequest then pass it on to the supervisor(HR) // so that he can process else { Supervisor.ApplyLeave(EmployeeName, NumberOfDaysLeave); } } } }
HR:
Create a class file named HR.cs and copy and paste the following code. As you can see, the MAX_LEAVES_CAN_APPROVE variable holds the maximum leave value the HR can approve. As part of the ApplyLeave method, we check whether HR approves the leave. If yes, it calls the ApproveLeave method to approve the leave. If not, then it will print that your leave application is suspended.
using System; namespace ChainOfResponsibilityDesignPatternExample { //Concrete Handler 3 //The following class implement the EmployeeLeaveHandler abstract class and //Provide Implementation for ApplyLeave abstract method public class HR : EmployeeLeaveHandler { // HR can only approve up to 30 days of leave private readonly int MAX_LEAVES_CAN_APPROVE = 30; public override void ApplyLeave(string EmployeeName, int NumberOfDaysLeave) { if (NumberOfDaysLeave <= MAX_LEAVES_CAN_APPROVE) { Console.WriteLine($"HR Approved {NumberOfDaysLeave} Days Leave for the employee {EmployeeName}"); } else { Console.WriteLine($"Leave Application Suspended for Employee {EmployeeName}, Please contact HR"); } } } }
Step3: Client
The Main method of the Program class is going to be the Client. Please modify the Main method of your application as shown below. First, we are creating the hierarchy by setting the next level supervisor.
using System; namespace ChainOfResponsibilityDesignPatternExample { class Program { static void Main(string[] args) { TeamLeader teamLeader = new TeamLeader(); ProjectLeader projectLeader = new ProjectLeader(); HR hr = new HR(); teamLeader.SetNextSupervisor(projectLeader); projectLeader.SetNextSupervisor(hr); teamLeader.ApplyLeave("Anurag", 9); Console.WriteLine(); teamLeader.ApplyLeave("Pranaya", 18); Console.WriteLine(); teamLeader.ApplyLeave("Priyanka", 30); Console.WriteLine(); teamLeader.ApplyLeave("Ramesh", 50); Console.Read(); } } }
Output:
Advantages of Chain of Responsibility Design Pattern:
- Decoupling: It decouples the sender of the request and its receivers. The sender doesn’t need to know which part of the chain will handle the request.
- Dynamic Handling: Handlers in the chain can be dynamically added or changed.
- Flexibility: Gives more flexibility in distributing responsibilities among objects.
- Responsibility Segregation: It allows dividing the processing or handling responsibilities among different objects.
Use Cases of Responsibility Design Pattern:
We need to use the Chain of Responsibility Design Pattern in Real-Time Applications in the following scenarios:
- Multiple Handlers for a Request: When multiple objects can handle a request, but the specific handler isn’t known in advance, the request can be passed along a chain of handlers until one handles it.
- Decoupling Request Senders and Receivers: If you want to decouple the sender of a request from its receivers. The sender doesn’t need to know which part of the chain will handle the request, promoting loose coupling in the system.
- Conditional Handling of Requests: In scenarios where the handling of a request depends on a set of conditions or criteria that only the handlers can evaluate. Each handler in the chain can decide whether to process the request or pass it along.
- Dynamic Handling: If the handling logic needs to be changed or reconfigured dynamically at runtime, the Chain of Responsibility allows for adding or removing handlers from the chain.
- Grouping Related Handlers Together: When you want to group several related handlers together. For instance, different handlers can perform different validations in a sequence.
- Preventing Direct Coupling: To avoid direct coupling between the sender of a request and its receivers, thereby allowing an object chain to operate independently.
- Creating an Audit Trail: In some cases, especially for logging or auditing purposes, you might want multiple objects to process the same request, with each object performing its own operation.
- Complex Validation or Authorization Processes: When a request requires a series of validation or authorization steps before it can be processed. Each step in this process can be encapsulated in a handler.
Exception Handling in C# is one of the best examples of the Chain of Responsibility Design Pattern. When an exception is thrown from the try block, then that exception might be handled by a corresponding catch block. Here, you can have more than one catch block, and the catch blocks will behave like handlers to handle the exception.
In the next article, I will discuss the Chain of Responsibility Design Pattern with one Receiver Handling the Request with one Real-Time Example using C#. In this article, I explain the Chain of Responsibility Design Pattern in C# with Examples. I hope you understand the need and use of the Chain of Responsibility Design Pattern Examples in C#.