The Chain of Responsibility pattern is a behavioural design pattern that allows an object to pass a request along a chain of potential handlers. Upon receiving a request, each handler decides to process the request or pass it to the next handler in the chain. This pattern decouples the sender of a request from its receiver, giving multiple objects a chance to handle the request without the sender knowing which object will ultimately process it.
Let's see this in detail with an example using c# and a story.
You can download the full source code here
Story Begins
Once upon a time in a faraway land, John had the challenge of creating an expense approval system for a kingdom ruled by financial prudence.
In this kingdom, expenses needed approval from various levels of authority, each with its own limit. Alex, seeking a solution that embodied elegance and flexibility, embarked on a journey guided by the ancient wisdom of the Chain of Responsibility pattern.
The Hierarchical Hurdles
The kingdom had three levels of authority: Managers, Directors, and the Chief Financial Officer (CFO). Each handler in this hierarchy had the power to approve expenses up to a certain limit. The journey began with the creation of these wise handlers:
The Manager:
The first line of defence in the approval hierarchy.
Capable of approving expenses up to $1000.
The Director:
A higher authority, ready to tackle more significant expenses.
Could approve expenses up to $5000.
The CFO:
The pinnacle of financial wisdom.
Could approve expenses up to $10000.
Forming Chain of Responsibility
With the handlers in place, Alex connected them in a chain, each aware of its place in the hierarchy. The Chain of Responsibility pattern was weaving its magic, creating a dynamic system capable of handling expenses with varying levels of complexity.
Manager manager = new Manager();
Director director = new Director();
CFO cfo = new CFO();
manager.SetNextHandler(director);
director.SetNextHandler(cfo);
The chain was set, and the kingdom awaited the arrival of expense requests seeking approval.
Expense Handler Logic
/// <summary>
/// Expense handler class
/// </summary>
public abstract class ExpenseHandler
{
protected ExpenseHandler? NextHandler;
public void SetNextHandler(ExpenseHandler nextHandler)
{
NextHandler = nextHandler;
}
public abstract void ApproveExpense(Expense expense);
}
Logic handling the approval flow
/// <summary>
/// Manager level ExpenseHandler
/// </summary>
public class Manager : ExpenseHandler
{
public override void ApproveExpense(Expense expense)
{
if (expense.Amount <= 1000)
{
Console.WriteLine($"Approved amount:- ${expense.Amount} by Manager");
}
else
{
NextHandler?.ApproveExpense(expense);
}
}
}
/// <summary>
/// Director level Expense Handler
/// </summary>
public class Director : ExpenseHandler
{
public override void ApproveExpense(Expense expense)
{
if (expense.Amount <= 5000)
{
Console.WriteLine($"Approved amount:- ${expense.Amount} by Director.");
}
else
{
NextHandler?.ApproveExpense(expense);
}
}
}
/// <summary>
/// CFO level Expense Handler
/// </summary>
public class CFO : ExpenseHandler
{
public override void ApproveExpense(Expense expense)
{
if (expense.Amount <= 10000)
{
Console.WriteLine($"Approved amount:- ${expense.Amount} by CFO");
}
else
{
Console.WriteLine($"Amout:- ${expense.Amount} : This is too expensive and can not be approved by just CFO, this need approval from the King!!!");
}
}
}
The Dance of Expenses
As the first rays of approval requests dawned upon the kingdom, the Chain of Responsibility pattern sprang into action. The client, representing the kingdom's financial requests, initiated the journey.
// Client initiates the request
Expense expense1 = new() { Description = "Team Lunch", Amount = 800 };
manager.ApproveExpense(expense1);
Expense expense2 = new() { Description = "Projector Purchase", Amount = 5500 };
manager.ApproveExpense(expense2);
Expense expense3 = new() { Description = "Conference Sponsorship", Amount = 12000 };
manager.ApproveExpense(expense3);
The Manager handled the modest lunch expenses with ease, passing the baton to the Director when faced with a projector purchase. The Director, in turn, gracefully redirected the request to the CFO when the expense soared beyond their limit.
Epilogue
In the end, the kingdom marvelled at the wisdom of the Chain of Responsibility pattern. Alex, having woven a tale of approval intricacies, had bestowed upon the kingdom a system that could adapt and flex as financial needs evolved.
The Chain of Responsibility pattern, with its dynamic dance of handlers, proved to be a guiding force in the kingdom's journey toward financial prudence. As the sun set on this digital odyssey, the kingdom rested assured that it possessed a powerful tool, capable of navigating the complexities of expense approvals with grace and flexibility. And so, the tale of Softwareland's Odyssey of Approval lived on, inspiring developers to weave patterns of elegance and adaptability into their digital creations.
Commenti