Design Patterns Simplified - Chain Of Responsibility

This article explains what Chain of Responsibility Design Pattern is and how to use it in software development.

I am here to discuss one of the popular behavioral design patterns, called Chain of Responsibility. Before going through its implementation, let’s begin by defining it.

As per GOF guys, Chain of Responsibility pattern is defined as following.

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.

Well! Let’s understand what they mean and where this pattern can fit.

It simply means that this pattern is about having workflow kind of setup where any request goes from one to another based on certain logic. Some of the use cases can be as follows.

  • Leave approval mail from Team lead -> Project Lead -> Delivery Manager -> Director
  • Support tickets resolution from Level 0 -> Level 1 -> Level 2 -> Level 3
  • Loan approval form Clerk -> Assist Manager -> Manager
  • Asset purchase approval from Director -> Vice President -> President
  • Content approval from Proof Reader -> Editor -> Senior Editor

Another example of this pattern in .NET framework is the execution of catch statements in the try...catch block. Here, each catch block considers the request in the order and pass on to the next, if not able to handle.

Now, let’s see the custom implementation of the pattern. Please have a look at the code map diagram of the demo we have created.

Design patterns

As you can see, Chain of Responsibility pattern has some key components.

  • Handler -> Interface/abstract class to handle the request and set the successor or next approver link. In the diagram above Approver abstract class does this job.
  • ConcreteHandler -> Provides the implementation to the hander. Here TeamLead, ProjectLead, DeliveryManager and Director classes have been used for this.
  • Client -> Creates the request and passes on to the respective concrete handler in the chain. Here Client class is there to serve the purpose.

How Chain of Responsibility pattern works:

Let’s understand this by a demo project we have created.

First, create the Approver abstract class.

  1. abstract class Approver  
  2.     {  
  3.         protected Approver NextApprover;  
  4.         public abstract void ApproveLeave(int numberOfLeaves);      
  5.         public void SetNextApprover(Approver nextApprover)  
  6.         {  
  7.             this.NextApprover = nextApprover;  
  8.         }  
  9.         protected abstract bool CanApprove(int numberOfLeaves);  
  10.     }  

As you can see, successor is being set via SetNextApprover method and NextApprover object stores it.

Now, we need to create concrete classes where we will provide the implementation to the abstract methods.

  1. class TeamLead : Approver  
  2.     {  
  3.         public override void ApproveLeave(int numberOfLeaves)  
  4.         {  
  5.             if (CanApprove(numberOfLeaves))  
  6.             {  
  7.                 Util.PrintLeaveMesage(this.GetType().Name, numberOfLeaves);  
  8.             }  
  9.             else  
  10.             {  
  11.                 Util.SetNextApproverElsePrint(this, numberOfLeaves);                 
  12.             }  
  13.         }  
  14.   
  15.         protected override bool CanApprove(int numberOfLeaves)  
  16.         {  
  17.             if (numberOfLeaves <=3)  
  18.             {  
  19.                 return true;  
  20.             }  
  21.             return false;  
  22.         }  
  23.     }  
  24. class ProjectLead : Approver  
  25.     {  
  26.         public override void ApproveLeave(int numberOfLeaves)  
  27.         {  
  28.             //Omitted for simplicity, Same as TeamLead class  
  29.         }  
  30.   
  31.         protected override bool CanApprove(int numberOfLeaves)  
  32.         {  
  33.             if (numberOfLeaves > 3 && numberOfLeaves <= 10)  
  34.             {  
  35.                 return true;  
  36.             }  
  37.             return false;  
  38.         }  
  39.     }  
  40. class DeliveryManager : Approver  
  41.     {  
  42.         public override void ApproveLeave(int numberOfLeaves)  
  43.         {  
  44.             //Omitted for simplicity, Same as TeamLead class  
  45.         }  
  46.   
  47.         protected override bool CanApprove(int numberOfLeaves)  
  48.         {  
  49.             if (numberOfLeaves > 10 && numberOfLeaves <= 15)  
  50.             {  
  51.                 return true;  
  52.             }  
  53.             return false;  
  54.         }  
  55.     }  
  56. class Director : Approver  
  57.     {  
  58.         public override void ApproveLeave(int numberOfLeaves)  
  59.         {  
  60.             //Omitted for simplicity, Same as TeamLead class          
  61.         }  
  62.   
  63.         protected override bool CanApprove(int numberOfLeaves)  
  64.         {  
  65.             if (numberOfLeaves > 15 && numberOfLeaves <= 20)  
  66.             {  
  67.                 return true;  
  68.             }  
  69.             return false;  
  70.         }  
  71.     }  

We have also created utility class (Util.cs) to enforce the code reusability and avoid duplication of code. The definition goes as below.

  1. class Util  
  2.     {  
  3.         public static void PrintLeaveMesage(string approver, int numberOfLeaves)  
  4.         {  
  5.             Console.WriteLine(approver + $" has approved the leave(s) applied for {numberOfLeaves} day(s)");  
  6.         }  
  7.   
  8.         public static void SetNextApproverElsePrint(Approver approver, int numberOfLeaves)  
  9.         {  
  10.             if (approver.NextApprover != null)  
  11.             {  
  12.                 approver.NextApprover.ApproveLeave(numberOfLeaves);  
  13.             }  
  14.             else  
  15.             {  
  16.                 Console.WriteLine("Your leaves require special approval, please contact HR dept.");  
  17.             }  
  18.         }  
  19.     }  

As you can see in all the four concrete classes we have given the custom definition of the CanApprove method based on the specification or characteristics of the approver. If any receiver is able to approve, the request ends there else it goes to the next approver in the chain.

In Util class, we are printing the user-friendly messages and setting the next approver.

Now it’s time to setup client. Let put the code for it.

  1. class Client  
  2.     {  
  3.         static void Main(string[] args)  
  4.         {  
  5.             Console.Title = "Chain of Responsibility Demo";  
  6.   
  7.             TeamLead tl = new TeamLead();  
  8.             ProjectLead pl = new ProjectLead();  
  9.             DeliveryManager dm = new DeliveryManager();  
  10.             Director d = new Director();  
  11.             tl.SetNextApprover(pl);  
  12.             pl.SetNextApprover(dm);  
  13.             dm.SetNextApprover(d);  
  14.   
  15.             tl.ApproveLeave(3);  
  16.             tl.ApproveLeave(7);  
  17.             tl.ApproveLeave(12);  
  18.             tl.ApproveLeave(17);  
  19.             tl.ApproveLeave(22);  
  20.         }  
  21.     }  

And here goes the output of the demo we have developed.

Design patterns

Conclusion

In the article, we have gone through what Chain of Responsibility pattern is, when and how to use it. So, to summarize, it can be used in places where you want to pass on the request to the set of handlers at runtime. You can also download the attached demo project (CORPatternDemo.zip) to go through the full source code referred in the article.

Hope you have liked the article. Look forward to your comments/suggestions.