Chain of responsibility design pattern is part of the behavioral design patterns. It defines systematic handling of the requests in a chain, with a number of intermediates.
What is Chain of responsibility design pattern ?
According to GoF’s definition, this pattern is :
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.
A real world example :
Suppose you want to have some leaves from your job. The company has a policy that your immediate reporting manager can approve maximum of 2 leaves for you. For 3-10 leaves, he cannot do it and needs to pass on the request to your project manager and for more then 10 days, your project manager has no right to do so and must pass it on to the higher management (tough policy…huhhh). And finally you get the approval (at last…!!!). So in this case, you act as a client who request for leave and your managers act as the request handlers to form a chain of responsibility. Though, its not necessary for a hierarchy to exist for the request handling. Its the kind of conditions, that implicitly make it a hierarchy concept.
Let’s get technical :
Let’s try to implement this concept. So for this, we will be requiring the following components in the system.
1. Client : The class that request for something. In our case, its an employee, who will be requesting for a leave.
2. Request Handling Class : Could be an interface or an abstract class(preferably abstract class). Will discuss why to use abstract class. This class will be have the abstract method to handle the requests it receives, and a concrete method, to set the next request handler, in case the current handler cannot handle the request.
3. Concrete Request Handlers : These are the actual request handlers implementing the request handling class method, to manage the request which they receive, in their own ways. So let’s get started.
Our Request handling class will be
- LeaveSettings, is a simple class to handle the settings of the leave applier.
- LeaveHandler, is the abstract class with an abstract method, ProcessLeave, which will be implemented by different different handlers in their own way.
- SetNextLeaveHandler, is the method common for all the classes, to set their next successor, in case they are having a restriction in handling the request. So we will be setting these successors, for each level, when we make the first request to the immediate request handler. Will discuss this in detail further.
Our next step will be creating the request handlers, to manage the request in their own way. So our code will be like the below :
As we can see above, we have three different request handlers, i.e. the ReportingManager, ProjectManager and Management. The three of them will handle their requests in their own ways or else, will pass it on to their next handler.
Finally, we will be applying for the leave, as three different people, for three different duration of time. So our code will be:
So, we are applying for the leave to the reporting manager, and setting the further request handlers, so that every type of leave request can be handled. Now run the application and see the results.
So what happened behind the scenes is that when the first user applied for leave, his condition was satisfied by the ProcessLeave method and was completed. For second user however, the request was not meeting the criteria of the reporting manager, so it passed the next request to the ProjectManager, using the else condition _nextLeaveApprover.ProcessLeave(leaveSettings); (in the second image).
Now, how does the _nextLeaveApprover knows that next handler is the ProjectManager ? This was through the rManager.SetNextLeaveHandler(pManager); in the third image, where we had set the next request handler. So this is how the thir user request is handled. and the command propagates to all levels.
Now the point that why we are using the abstract class for the handle. We could have used the interface(I also did it first time). The reason is that using the next successor is an optional thing. We need not set any successor for the final handler. So implementing this successor method is just a waste code for the last level of request handler. Also it is not necessary that we will be passing the requests to the next level all the time. So we created a concrete method in the base class with options for each level to set their successors.
So this was all about this design pattern.