Understanding and Implementing Chain of Responsibility Pattern in C#

This article talks about the Chain of responsibility pattern. We will try to see when this pattern can be useful and what are the benefits of using this pattern. We will also look at a rudimentary implementation of Chain of responsibility pattern in C#.

Background

We have seen the scenarios where one class need to know about the status change of another class while looking at the Observer pattern. In Observer pattern a class can register itself to another class to get the notification whenever the state of other class is changing. The notification will come in form of an event and the listener class could then decide what action it needs to take.

Now if we take this scenario a step further where the class listening to events will take the decision based on some conditions. If the condition is not met then it will further pass on this event to another object that could handle this event.

So in this scenario we have a series of objects that can handle the event based on some criteria. These all object will pass on the event to others in a sequential manner. Whether this class could take the action or it need to send the event further is the logic that will be contained in this class. All the classes that can handle the event are chained together i.e. ever class contains a handle to its successor to which it can pass on the event to.

Chain of responsibility pattern is meant for such scenarios. In this pattern an object will listen for an event, when this event occurs it handles the event. if this object is capable of taking some action, it will otherwise it will propagate the event to another object in line which could in turn handle the event or propagate it further. This pattern is very useful in implementing workflow kind of scenarios.

GoF defines Chain of responsibility pattern as “Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.”

GofCOR

To understand this class diagram lets look at each class.

  • Handler: This is the interface or abstract class that all the classes who could handle the event should implement.
  • ConcreteHandler: There are the classes that are capable of either handling the event or propogating it further.
  • Client: This is the class that defines the chain i.e. successors for all ConcreteHandlersand this is the class that initiates the request to a ConcreteHandler.

Using the code

To understand this pattern better, lets try to implement a small rudimentary work flow application.

Lets say we have an organization where Team members when apply for leave, the request goes to the Team Leader. Team leader can approve all the leave request of less than 10 days. If the leave request is of more than 10 days then the request will be passed on to the Project Leader. Project leader is able to approve leaves of upto 20 days. If the leave is applied for more than 20 days then this requests will be passed to the HR. HR can approve upto 30 days of leave. If the leave is of more than 30 days then the leave application cannot be approved by the system and it needs a manual process for approval.

Now with the above requirement, if we try to implement the Chain of responsibility pattern then we need ConcreteHandlersfor Team Leader, Project Leader and HR. These ConcreteHandlerswill be chained together so that the request can pass from TeamLeaderto ProjectLeaderto HR.

Also, we need an abstract class that can contain the common functionality like keeping track oc successor object, initiating the request and the eventing mechanism. Lets call this class Employee. The ConcreteHandlerwill contain logic specific to the concrete handlers.

Lets start by looking at the Handlerabstract class i.e. Employeeclass.

This Handlerclass is responsible for:

  1. Keeping track of successors for this object
  2. Implementing the eventing mechanism to notify and propagate the request event.
  3. Initiate the request.

Now since our Handler class is ready, lets look at the ConcreteHandlersone by one. Lets start with TeamLeaderclass.

What this class is doing is:

  1. Checking of this class could take the action i.e. leave applied is less than 10 days.
  2. If this class could take the action then show the response to the user.
  3. If this class is not able to take the action then pass on the request to the ProjectLeaderclass i.e. its successor.

Now lets look at the ProjectLeaderclass.

What this class is doing is:

  1. Checking of this class could take the action i.e. leave applied is less than 20 days.
  2. If this class could take the action then show the response to the user.
  3. If this class is not able to take the action then pass on the request to the HRclass i.e. its successor.

Now lets look at the HRclass.

What this class is doing is:

  1. Checking of this class could take the action i.e. leave applied is less than 30 days.
  2. If this class could take the action then show the response to the user.
  3. If this class is not able to take the action then let the user know that he needs to have a manual discussion and his request has been suspended.

The actual action item i.e. the leave is encapsulated into a class for better modularity, so before running the application lets see how this Leaveclass looks like

And finally we need the Clientthat will set the successor chain and initiate the request.

So we can see that this Main function is creating the chain by setting the successors for each class and is initiating the request to TeamLeaderclass. One request for each scenario has been made. When we run this application.

RunningCOR

So we can see that each request get passed on and processed by the respective class based on the number of days applied for. Before wrapping up let us look at the class diagram for our application and compare it with the original GoF diagram.

myCOR

Point of interest

In this article, we tried to look at the Chain of responsibility pattern. We saw when this pattern could be useful, what are the benefits of using this pattern and how can we have a sample rudimentary implementation for this pattern in C#. This article has been written from a beginner’s perspective. I hope this has been informative.

Download sample code for this article: ChainOfResponsibilityDemo