Understanding and Implementing Chain of Responsibility Pattern in C#






4.36/5 (9 votes)
This article talks about the Chain of responsibility pattern.
Introduction
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."

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 allConcreteHandlers
and this is the class that initiates the request to aConcreteHandler
.
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
ConcreteHandlers
for Team Leader, Project Leader and HR. These ConcreteHandlers
will be chained together so that
the request can pass from TeamLeader
to ProjectLeader
to 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 ConcreteHandler
will
contain logic specific to the concrete handlers.
Lets start by looking at the Handler
abstract class i.e. Employee
class.
public abstract class Employee
{
// Every employee will have a supervisor
protected Employee supervisor;
// Event mechanism to know whenever a leave has been applied
public delegate void OnLeaveApplied(Employee e, Leave l);
public event OnLeaveApplied onLeaveApplied = null;
// This will invoke events when the leave will be applied
// i.e. the actual item will be handed over to the hierarchy of
// concrete handlers.
public void LeaveApplied(Employee s, Leave leave)
{
if (onLeaveApplied != null)
{
onLeaveApplied(this, leave);
}
}
// This is the function which concrete handlers will use to take
// action, if they are able to take actions.
public abstract void ApproveLeave(Leave leave);
// getter to get the supervisor of current employee
public Employee Supervisor
{
get
{
return supervisor;
}
set
{
supervisor = value;
}
}
// Using this we can apply for leave
public void ApplyLeave(Leave l)
{
LeaveApplied(this, l);
}
}
This Handler
class is responsible for:
- Keeping track of successors for this object
- Implementing the eventing mechanism to notify and propagate the request event.
- Initiate the request.
Now since our Handler class is ready, lets look at the ConcreteHandlers
one by one. Lets start with
TeamLeader
class.
public class TeamLeader : Employee
{
// team leas can only approve upto 7 days of leave
const int MAX_LEAVES_CAN_APPROVE = 10;
// in constructor we will attach the event handler that
// will check if this employee can process or he need to
// pass on to next employee
public TeamLeader()
{
this.onLeaveApplied += new OnLeaveApplied(TeamLeader_onLeaveApplied);
}
// in this function we will check if this employee can
// process or he need to pass on to next employee
void TeamLeader_onLeaveApplied(Employee e, Leave l)
{
// check if we can process this request
if (l.NumberOfDays < MAX_LEAVES_CAN_APPROVE)
{
// process it on our level only
ApproveLeave(l);
}
else
{
// if we cant process pass on to the supervisor
// so that he can process
if (Supervisor != null)
{
Supervisor.LeaveApplied(this, l);
}
}
}
// If we can process lets show the output
public override void ApproveLeave(Leave leave)
{
Console.WriteLine("LeaveID: {0} Days: {1} Approver: {2}",
leave.LeaveID, leave.NumberOfDays, "Team Leader");
}
}
What this class is doing is:
- Checking of this class could take the action i.e. leave applied is less than 10 days.
- If this class could take the action then show the response to the user.
- If this class is not able to take the action then pass on the request to the
ProjectLeader
class i.e. its successor.
Now lets look at the ProjectLeader
class.
class ProjectLeader : Employee
{
const int MAX_LEAVES_CAN_APPROVE = 20;
// in constructor we will attach the event handler that
// will check if this employee can process or he need to
// pass on to next employee
public ProjectLeader()
{
this.onLeaveApplied += new OnLeaveApplied(ProjectLeader_onLeaveApplied);
}
// in this function we will check if this employee can
// process or he need to pass on to next employee
void ProjectLeader_onLeaveApplied(Employee e, Leave l)
{
// check if we can process this request
if (l.NumberOfDays < MAX_LEAVES_CAN_APPROVE)
{
// process it on our level only
ApproveLeave(l);
}
else
{
// if we cant process pass on to the supervisor
// so that he can process
if (Supervisor != null)
{
Supervisor.LeaveApplied(this, l);
}
}
}
// If we can process lets show the output
public override void ApproveLeave(Leave leave)
{
Console.WriteLine("LeaveID: {0} Days: {1} Approver: {2}",
leave.LeaveID, leave.NumberOfDays, "Project Leader");
}
}
What this class is doing is:
- Checking of this class could take the action i.e. leave applied is less than 20 days.
- If this class could take the action then show the response to the user.
- If this class is not able to take the action then pass on the request to the
HR
class i.e. its successor.
Now lets look at the HR
class.
class HR : Employee
{
const int MAX_LEAVES_CAN_APPROVE = 30;
// in constructor we will attach the event handler that
// will check if this employee can process or
// some other action is needed
public HR()
{
this.onLeaveApplied += new OnLeaveApplied(HR_onLeaveApplied);
}
// in this function we will check if this employee can
// process or some other action is needed
void HR_onLeaveApplied(Employee e, Leave l)
{
// check if we can process this request
if (l.NumberOfDays < MAX_LEAVES_CAN_APPROVE)
{
// process it on our level only
ApproveLeave(l);
}
else
{
// if we cant process pass on to the supervisor
// so that he can process
if (Supervisor != null)
{
Supervisor.LeaveApplied(this, l);
}
else
{
// There is no one up in hierarchy so lets
// tell the user what he needs to do now
Console.WriteLine("Leave application suspended, Please contact HR");
}
}
}
// If we can process lets show the output
public override void ApproveLeave(Leave leave)
{
Console.WriteLine("LeaveID: {0} Days: {1} Approver: {2}",
leave.LeaveID, leave.NumberOfDays, "HR");
}
}
What this class is doing is:
- Checking of this class could take the action i.e. leave applied is less than 30 days.
- If this class could take the action then show the response to the user.
- 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 Leave
class looks like
// This is the actual Item that will be used by the concretehandlers
// to determine whther they can act upon this request or not
public class Leave
{
public Leave(Guid guid, int days)
{
leaveID = guid;
numberOfDays = days;
}
Guid leaveID;
public Guid LeaveID
{
get { return leaveID; }
set { leaveID = value; }
}
int numberOfDays;
public int NumberOfDays
{
get { return numberOfDays; }
set { numberOfDays = value; }
}
}
And finally we need the Client
that will set the successor chain and initiate the request.
class Program
{
static void Main(string[] args)
{
// lets create employees
TeamLeader tl = new TeamLeader();
ProjectLeader pl = new ProjectLeader();
HR hr = new HR();
// Now lets set the hierarchy of employees
tl.Supervisor = pl;
pl.Supervisor = hr;
// Now lets apply 5 day leave my TL
tl.ApplyLeave(new Leave(Guid.NewGuid(), 5));
// Now lets apply 15 day leave my TL
tl.ApplyLeave(new Leave(Guid.NewGuid(), 15));
// Now lets apply 25 day leave my TL
tl.ApplyLeave(new Leave(Guid.NewGuid(), 25));
// Now lets apply 35 day leave my TL
tl.ApplyLeave(new Leave(Guid.NewGuid(), 35));
Console.ReadLine();
}
}
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 TeamLeader
class. One request for each scenario has been made. When
we run this application.

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.

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.
History
- 16 November 2012: First version