Introduction
When it comes to design patterns in object-oriented
programming, it is very hard to find where we can apply such patterns and what
problem exactly does it solve with some real-world example. In my series of articles,
I would like to cover bad code without patterns and how to apply design
patterns in that properly to make better code. In this article, I have covered
the chain of responsibility behavioral design pattern. Let us look into this.
Definition
It is the most important design pattern that falls under the
behavioral category as per gang of four. It defines that “avoid coupling from the
sender of a request to its receiver by adding one more object to handle the
request. Chain the receiving objects and pass the request along the chain until
an object handles it.”
Scenario
Suppose you are a Developer and you want to apply a leave
request in your company. The company policy for Developer says that if leave request
for 1-day team lead will handle if leave request for more than 1-day manager
will handle and if left for more than a 10 days director will handle.
Pseudocode
public static void Main(string[] args){
var leaveRequest = new LeaveRequest() { NoOfDays = 1 }; // assume
this is an request that comes
dynamically
if(leaveRequest.NoOfDays==1)
{
//team
lead will handle the request
}
else if(leaveRequest.NoOfDays
> 1)
{
//manager
will handle the request
}
else if
(leaveRequest.NoOfDays > 10 )
{
//director
will handle the request
}
}
Let us think in OOP’s way
UML Diagram
Participants
·
ILeaveRequestHandler (Handler)
o
Defines an interface to handle the request
·
Team leadHandler(Receiver)
o One
of the objects in the chain of receiver objects.
o It
will Process leave request if it is only one day else it will set next receiver
object- manager(successor)
·
ManagerHandler(Receiver)
o One
of the objects in the chain of receiver objects.
o It
will Process leave request if it is more than one day else it will set next
receiver object-director(successor)
·
DirectorHandler(Receiver)
o One
of the objects in the chain of receiver objects.
o Last
object in the chain of receiver object and it will not set any receiver object
in the chain
·
Client(Sender)
o Sender
for the leave Request
Receiver Code
public class LeaveRequest
{
public int
EmployeeId { get; set; }
public int NoOfDays
{ get; set; }
}
public interface ILeaveRequestHandler
{
void Process(LeaveRequest leaveRequest);
}
public class TeamLeadHandler : ILeaveRequestHandler
{
public void
Process(LeaveRequest leaveRequest)
{
//team lead
will handle the request
}
}
public class ManagerHandler : ILeaveRequestHandler
{
public void
Process(LeaveRequest leaveRequest)
{
//manager will
handle the request
}
}
public class DirectorHandler : ILeaveRequestHandler
{
public void
Process(LeaveRequest leaveRequest)
{
// director
will handle the request
}
}
Client/Sender Code
class Program
{
//Sender for
the request
static void Main(string[] args)
{
var leaveRequest = new LeaveRequest() { EmployeeId = 1, NoOfDays = 2 }; // assume this
request comes from employee object
dynamically
if (leaveRequest.NoOfDays==1)
{
new
TeamLeadHandler().Process(leaveRequest); // assume that
these new objects are created from
dependency injection
}
else if(leaveRequest.NoOfDays
>1)
{
new ManagerHandler().Process(leaveRequest); // assume that these new objects
are created from dependency injection
}
else if
(leaveRequest.NoOfDays > 10)
{
new DirectorHandler().Process(leaveRequest); // assume that these objects
are created from dependency injection
}
}
}
Whats’s wrong with OOP’s code
Everyone will think that there is no problem with oops code
and it will work perfectly, right. suppose consider if any request rule change
in the company . we straight away change
if else rule in the client code and it’s done and it also met SINGLE
RESPONSIBILITY PRINCIPLE as shown below,
Modified/Changed Scenario
Modified leave
request rule in your company. The company policy for Developer says that if
leave request for 1-day team lead will handle if leave request for more than
1-day manager will handle and if left for more than a 5 days director will
handle.
Modified/Changed Pseudocode
public static void Main(string[] args){
var leaveRequest = new LeaveRequest() { NoOfDays = 1 }; // assume
this is an request that comes
dynamically
if(leaveRequest.NoOfDays==1)
{
//team
lead will handle the request
}
else if(leaveRequest.NoOfDays
> 1)
{
//manager
will handle the request
}
else if
(leaveRequest.NoOfDays > 5 ) //Modified rule
{
//director will handle the
request
}
}
If you look deeply into the code sender/client is tightly
coupled here. Any change in request rule you always depend on client code for a
change. In other words, the request is tightly coupled with the sender /client
and receiver.
Let us think in Design Pattern’s way
Problem
As mentioned in OOP’s way we found that there is a tight
coupling between the sender of the request and its receiver. let us understand how
the chain of responsibility pattern comes to rescue such case
Solution
According to the definition, we are going to add one more
object in order to decouple the sender of request to its receiver and let the receiver
itself decides whom to handle the request next if it can't handle the request.
UML Diagram
Participants
·
ILeaveRequestHandler (Handler)
o
Defines an interface to handle the request
·
LeaveRequestBaseHandler(HandlerBase)
o Implements
the handler interface and have a successor link in it.
·
team leadHandler(Receiver)
o One
of the objects in the chain of receiver objects.
o It
will Process leave request if it is only one day else it will set next receiver
object- manager(successor)
·
ManagerHandler(Receiver)
o One
of the objects in the chain of receiver objects.
o It
will Process leave request if it is more than one day else it will set next
receiver object-director(successor)
·
DirectorHandler(Receiver)
o One
of the objects in the chain of receiver objects.
o Last
object in the chain of receiver object and it will not set any receiver object
in the chain
Sample Code
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Chain of responsibility design pattern");
ILeaveRequestHandler
leaveRequestteamleadHandler = new TeamLeadHandler();
ILeaveRequestHandler
leaveRequestmanagerHandler = new ManagerHandler();
ILeaveRequestHandler
leaveRequestdirectorHandler = new DirectorHandler();
leaveRequestteamleadHandler.SetNextSuccessor(leaveRequestmanagerHandler);
leaveRequestmanagerHandler.SetNextSuccessor(leaveRequestdirectorHandler);
LeaveRequest leaveRequest = new LeaveRequest() { NoOfDays = 1 };
leaveRequestteamleadHandler.Process(leaveRequest);
LeaveRequest leaveRequest1 = new LeaveRequest() { NoOfDays = 2 };
leaveRequestteamleadHandler.Process(leaveRequest1);
LeaveRequest leaveRequest2 = new LeaveRequest() { NoOfDays = 31 };
leaveRequestteamleadHandler.Process(leaveRequest2);
Console.ReadKey();
}
}
public interface ILeaveRequestHandler
{
void SetNextSuccessor(ILeaveRequestHandler leaveRequestHandler);
void Process(LeaveRequest leaveRequest);
}
public abstract class LeaveRequestBaseHandler :
ILeaveRequestHandler
{
protected ILeaveRequestHandler _leaveRequestBaseHandler;
public abstract void
Process(LeaveRequest leaveRequest);
public void
SetNextSuccessor(ILeaveRequestHandler leaveRequestHandler)
{
this._leaveRequestBaseHandler = leaveRequestHandler;
}
}
public class TeamLeadHandler : LeaveRequestBaseHandler
{
public override void
Process(LeaveRequest leaveRequest)
{
if(leaveRequest.NoOfDays==1)
{
Console.WriteLine("Leave approved by team lead");
}
else
{
_leaveRequestBaseHandler.Process(leaveRequest);
}
}
}
public class ManagerHandler : LeaveRequestBaseHandler
{
public override void Process(LeaveRequest
leaveRequest)
{
if(leaveRequest.NoOfDays>30)
{
_leaveRequestBaseHandler.Process(leaveRequest);
}
else
{
Console.WriteLine("Leave approved by manager");
}
}
}
public class DirectorHandler : LeaveRequestBaseHandler
{
public override void
Process(LeaveRequest leaveRequest)
{
Console.WriteLine("Leave Approved by director");
}
}
public class LeaveRequest
{
public int NoOfDays
{ get; set; }
}
Comments
Post a Comment