Skip to main content

Fixing Multiple If-Else problem with Chain of Responsibility Pattern

 Situation will become unmanageable if we have lot of if else condition in our code, Lets understand problem and then we will see it’s Solution.

Problem with Multiple If Conditions

Using multiple if-else conditions to handle different cases can lead to code that is hard to read, maintain, and extend. Here’s a simple example using multiple if conditions in a leave management system.

We will implement below Business Logic for Leave management system

a. IF Leave < = 3, then Team Lead will approve

b. IF Leave < =5, then Project Manager will approve

c. IF Leave > 5, then HR Manager will approve

Request first go to team lead to check if Request can be handled by Team lead else it will go to Project Manager else request will go to HR manager

Example with Multiple If Conditions

public class LeaveRequest
{
public string EmployeeName { get; set; }
public int NumberOfDays { get; set; }
public string Reason { get; set; }
}

public class LeaveApprovalService
{
public void ApproveLeave(LeaveRequest request)
{
if (request.NumberOfDays <= 3)
{
Console.WriteLine($"Leave request by {request.EmployeeName} for {request.NumberOfDays} days approved by Team Lead.");
}
else if (request.NumberOfDays <= 5)
{
Console.WriteLine($"Leave request by {request.EmployeeName} for {request.NumberOfDays} days approved by Project Manager.");
}
else if (request.NumberOfDays > 5)
{
Console.WriteLine($"Leave request by {request.EmployeeName} for {request.NumberOfDays} days approved by HR Manager.");
}
else
{
Console.WriteLine("Leave request could not be processed.");
}
}
}

Problems in above code

  1. Maintainability: The method becomes harder to maintain as the logic grows.
  2. Single Responsibility Principle (SRP): The method does too much by handling all approval levels, violating the single responsibility principle.
  3. Testing : Each time we add new condition we need to test end to end to make sure nothing is breaking because we are making changes into existing class because we are breaking SRP.

Solution with Chain of Responsibility Pattern

Chain of Responsibility Pattern or Chain of Responsibility Method is a Behavioral Design Pattern, which allows an object to send a request to other objects without knowing who is going to handle it.

This pattern encourages loose coupling between sender and receiver, providing freedom in handling the request.

Implementation Details

Centralize handler

1. This centralizes handler provides a default implementation for handling the request, which includes logic to either process the request or pass it to the next handler in the chain if it cannot handle it.

2. This centralizes handler maintains a reference to the next handler in the chain. This allows each handler to pass the request along the chain if it cannot handle the request.

3. By defining how requests are passed from one handler to the next, the abstract handler encapsulates this logic, reducing duplication and simplifying the concrete handlers.

Code Implementation

Leave Request class

public class LeaveRequest
{
public string EmployeeName { get; set; }
public int NumberOfDays { get; set; }
public string Reason { get; set; }
}

This class contains details of employee who is takig leave and why and how many days

2. Centralize Handler Creation

public abstract class LeaveHandler
{
protected LeaveHandler NextHandler;

public void SetNextHandler(LeaveHandler nextHandler)
{
NextHandler = nextHandler;
}

public void HandleRequest(LeaveRequest request)
{
if (CanHandle(request.NumberOfDays))
{
ProcessRequest(request);
}
else if (NextHandler != null)
{
NextHandler.HandleRequest(request);
}
else
{
Console.WriteLine(
"Leave request could not be processed.");
}
}

protected abstract bool CanHandle(int numberOfDays);
protected abstract void ProcessRequest(LeaveRequest request);
}

3. Concrete Handlers

Here we will create separate classes for Team lead, HR manager, project manager as below.

public class TeamLead : LeaveHandler
{
protected override bool CanHandle(int numberOfDays) => numberOfDays <= 3;

protected override void ProcessRequest(LeaveRequest request)
{
Console.WriteLine($"Leave request by {request.EmployeeName} for {request.NumberOfDays} days approved by Team Lead.");
}
}

public class ProjectManager : LeaveHandler
{
protected override bool CanHandle(int numberOfDays) => numberOfDays <= 5;

protected override void ProcessRequest(LeaveRequest request)
{
Console.WriteLine($"Leave request by {request.EmployeeName} for {request.NumberOfDays} days approved by Project Manager.");
}
}

public class HRManager : LeaveHandler
{
protected override bool CanHandle(int numberOfDays) => numberOfDays > 5;

protected override void ProcessRequest(LeaveRequest request)
{
Console.WriteLine($"Leave request by {request.EmployeeName} for {request.NumberOfDays} days approved by HR Manager.");
}
}

Client code for testing

using Chainofresponbility;

Console.WriteLine("Welcome to .Net Labs !");
Console.WriteLine(".........................");
Console.WriteLine("Example of Chain of Responsibility !");
Console.WriteLine(".........................");
Console.WriteLine(".........................");
// Create handlers
var teamLead = new TeamLead();
var projectManager = new ProjectManager();
var hrManager = new HRManager();

// Set up the chain
teamLead.SetNextHandler(projectManager);
projectManager.SetNextHandler(hrManager);

// Create a leave request
var leaveRequest = new LeaveRequest
{
EmployeeName = "Devesh",
NumberOfDays = 4,
Reason = "Sick Leave"
};

// Process the request
teamLead.HandleRequest(leaveRequest);

// Another leave request
var anotherLeaveRequest = new LeaveRequest
{
EmployeeName = "Sanjay Singh",
NumberOfDays = 6,
Reason = "Vacation"
};

// Process the request
teamLead.HandleRequest(anotherLeaveRequest);

Console.ReadKey();

Code explanation

a. Set up chain -> Request will go to team lead and next handler will be project manager and project manager have next handler as Hr manager,

// Set up the chain
teamLead.SetNextHandler(projectManager);
projectManager.SetNextHandler(hrManager);

b. Processing Request

// Create a leave request
var leaveRequest = new LeaveRequest
{
EmployeeName = "Devesh",
NumberOfDays = 4,
Reason = "Sick Leave"
};

// Process the request
teamLead.HandleRequest(leaveRequest);

Output

We can see Leave request for devesh handled by Project manager because it have leave count =4

Also we can see Leave request for Sanjay handled by HR manager because it have leave count =6

Thanks

Happy Coding

Comments

Popular posts from this blog

Design Application using Clean Architecture

  Clean Architecture , introduced by  Robert C. Martin   (Uncle Bob), aims to create systems that are: Independent of frameworks Testable Independent of UI Independent of database Independent of any external agency Clean Architecture emphasizes separating the concerns of different parts of the system and ensuring that each part has a clear responsibility. .NET clean architecture as a development approach, available in a layered form to reduce dependency. The architecture consists of several layers, each with a specific role and dependencies that flow in one direction: from the outer layers to the inner layers. Inner most Layer is independent to its outer layer, means inner most layer does not have any references, it builds and stand independently. Dependency Rule The core principle of Clean Architecture is the  Dependency Rule , which states that source code dependencies can only point inward.  This means: Nothing in inner circle can depend on anything in an out...

How to Earn online from Medium.com

  As freelancer I started writing on medium (Tech blogs) from  Aug 2024   but I was already writing tech blogs on c-sharpcorner.com “The purpose of this post is to encourage new writers by helping them understand how they can earn as freelancers.” “Medium is a great platform for writers. When I started writing, I had no idea that I would reach the $100 benchmark in just four months. Now, I’m feeling self-inspired, as it has become a valuable source of income as a freelancer.” AUG 2024 ( $0 ) It was just beginning, and I posted 4 articles and had only few views 455 and 165 reads. During this time, I was learning about medium rules for writers and partner program. It will take some time to understand over all process. SEP 2024 ($6) As I started writing and I was getting few cents on Aug 2024, I posted 8 more articles in SEP, and it was good month because my blogs were getting noticed and I started getting claps etc. “This month, I went from $0 to $6, which was an achievemen...

Fixing High CPU Usage in .NET Core using Parallelism

  We will fix problem occurs due to incorrect use Parallelism in .NET Core To increase application performance sometimes we use process task or work extensively in Parallel and that may lead performance issue. When we do code, we should understand our requirement very carefully along with hardware resources those we have. We should code like that there should be less CPU usage and server should work without issues. In .NET Core, when you’re using parallelism (like Parallel.For, Task.WhenAll, or Task.Run), you might encounter performance issues if the number of concurrent threads or tasks exceeds what your system can efficiently handle. A common issue is high CPU usage or resource contention, which can degrade overall performance, cause excessive context switching, and even lead to thread starvation or deadlocks. Non members can access from here Effective Parallelism Strategies to Mitigate High CPU Usage in .NET Core We will fix problem occurs due to incorrect use Parallelism in .NE...