Definition
GoF -> A way of passing a request between a chain of objects
GoF -> A way of passing a request between a chain of objects
- The Chain of Responsibility pattern works with a list of Handler objects that have limitations on the nature of the requests they can deal with. If a handler object cannot handle a request, it passes it on to the next handler object in the chain. At the end of the chain, there can be either default or exceptional behavior.
- The pattern chains the receiving objects together, and then passes any request messages from object to object until it reaches an object capable of handling the message.
- Chain of Responsibility simplifies object interconnections. Instead of senders and receivers maintaining references to all candidate receivers, each sender keeps a single reference to the head of the chain, and each receiver keeps a single reference to its immediate successor in the chain.
- Sometimes, the request object has to go through the chain objects mandatorily, like Http pipeline.
- Sometimes only through few handlers.
Design
Let us take an example; we are conducting a GK context. We have three teams to answer the questions. Each team will have 10000 ms to answer the questions, and team one has to start the each question. If team one didn't answer the question, it will be passed on to the next team.
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.
UML Diagram
From GoF
Code
---------------- handler Base class ----------------------
public abstract class Handlerbase
{
public Handlerbase NextTeam { get; private set; }
public ContextObject Question { get; private set; }
Let us take an example; we are conducting a GK context. We have three teams to answer the questions. Each team will have 10000 ms to answer the questions, and team one has to start the each question. If team one didn't answer the question, it will be passed on to the next team.
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.
UML Diagram
From GoF
Code
---------------- handler Base class ----------------------
public abstract class Handlerbase
{
public Handlerbase NextTeam { get; private set; }
public ContextObject Question { get; private set; }
public Handlerbase(Handlerbase
nextHandler, ContextObject question)
{
NextTeam = nextHandler;
Question = question;
}
{
NextTeam = nextHandler;
Question = question;
}
public abstract
void HandleRequest();
}
}
---------------- queue
object one ----------------------
public
class TeamOne:
Handlerbase
{
public TeamOne(Handlerbase nextHandler, ContextObject question) : base(nextHandler, question) { }
{
public TeamOne(Handlerbase nextHandler, ContextObject question) : base(nextHandler, question) { }
public override
void HandleRequest()
{
Console.WriteLine("Question : {0}", Question.Question.ToString());
Console.WriteLine("*******************************************"));
{
Console.WriteLine("Question : {0}", Question.Question.ToString());
Console.WriteLine("*******************************************"));
Console.WriteLine("Wating
for team one to respond");
Thread.Sleep(10000);
Console.WriteLine("\t no response from team one.....");
Thread.Sleep(10000);
Console.WriteLine("\t no response from team one.....");
NextTeam.HandleRequest();
}
}
}
}
---------------- chain
object two ----------------------
public class
TeamTwo :
Handlerbase
{
public TeamTwo(Handlerbase nextHandler, ContextObject question) : base(nextHandler, question) { }
{
public TeamTwo(Handlerbase nextHandler, ContextObject question) : base(nextHandler, question) { }
public override
void HandleRequest()
{
Console.WriteLine("Wating for team two to respond");
{
Console.WriteLine("Wating for team two to respond");
Thread.Sleep(10000);
Console.WriteLine("\t
no response from team two.....");
NextTeam.HandleRequest();
}
}
NextTeam.HandleRequest();
}
}
---------------- chain
object tree ----------------------
public class
TeamThree :
Handlerbase
{
public TeamThree(Handlerbase nextHandler, ContextObject question) : base(nextHandler, question) { }
{
public TeamThree(Handlerbase nextHandler, ContextObject question) : base(nextHandler, question) { }
public override
void HandleRequest()
{
Console.WriteLine("Wating for team three to respond");
Thread.Sleep(10000);
Console.WriteLine("\t no response from team three as well .....");
}
}
{
Console.WriteLine("Wating for team three to respond");
Thread.Sleep(10000);
Console.WriteLine("\t no response from team three as well .....");
}
}
----------------
Request----------------------
public class ContextObject
{
public string Question { get; set; }
}
---------------- Client----------------------
static void Main(string[] args)
{
ContextObject question = new ContextObject() { Question = "Who is an ediat in your team?" };
public class ContextObject
{
public string Question { get; set; }
}
---------------- Client----------------------
static void Main(string[] args)
{
ContextObject question = new ContextObject() { Question = "Who is an ediat in your team?" };
TeamThree teamThree =
new TeamThree(null,
question);
TeamTwo teamTwo = new TeamTwo(teamThree, question);
TeamOne teamOne = new TeamOne(teamTwo, question);
TeamTwo teamTwo = new TeamTwo(teamThree, question);
TeamOne teamOne = new TeamOne(teamTwo, question);
teamOne.HandleRequest();
Console.ReadKey();
}
Console.ReadKey();
}
Another Example:
To consider a more formal example, assume a scenario where you’ve a
banking system, and you want to implement some kind of Loan approval.
The customer may request a loan, and if it is below a specific amount,
the cashier may approve it directly. If it is above the specified
amount, he might pass the request to his manager for approval.
So
you may use Chain Of Responsibility implementation to hand over the
request/command to the correct approver. For an example, consider this
implementation of the above Bank account scenario. Our business rule is
something like, a cashier can approve the request if the amount is
lesser than 1000 $$, other wise the approval should be passed to the
manager. The manager can approve the request if the amount is lesser
than 10,000 $$.
We have the following components.
- LoanRequest – A concrete request
- IRequestHandler – Abstract request handler implementation
- Concrete handlers like Cashier and Manager implements this
- Has a reference to the successor to pass the request
- Program – The main driver
To the code
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DesignPatternsCof
{
//Request
class LoanRequest
{
public string Customer { get; set; }
public decimal Amount { get; set; }
}
//Abstract Request Handler
interface IRequestHandler
{
string Name { get; set; }
void HandleRequest(LoanRequest req);
IRequestHandler Successor { get; set; }
}
//Just an extension method for the passing the request
static class RequestHandlerExtension
{
public static void TrySuccessor(this IRequestHandler current, LoanRequest req)
{
if (current.Successor != null)
{
Console.WriteLine("{0} Can't approve - Passing request to {1}", current.Name, current.Successor.Name);
current.Successor.HandleRequest(req);
}
else
{
Console.WriteLine("Amount invaid, no approval given");
}
}
}
//Concrete Request Handler - Cachier
//Cachier can approve requests upto 1000$$
class Cashier : IRequestHandler
{
public string Name { get; set; }
public void HandleRequest(LoanRequest req)
{
Console.WriteLine("\n----\n{0} $$ Loan Requested by {1}",
req.Amount, req.Customer);
if (req.Amount<1000)
Console.WriteLine("{0} $$ Loan approved for {1} - Approved by {2}",
req.Amount,req.Customer, this.Name);
else
this.TrySuccessor(req);
}
public IRequestHandler Successor { get; set; }
}
//Concrete Request Handler - Manager
//Manager can approve requests upto 10000$
class Manager : IRequestHandler
{
public string Name { get; set; }
public void HandleRequest(LoanRequest req)
{
if (req.Amount < 10000)
Console.WriteLine("{0} $$ Loan approved for {1} - Approved by {2}",
req.Amount, req.Customer, this.Name);
else
this.TrySuccessor(req);
}
public IRequestHandler Successor { get; set; }
}
//Main driver
class Program
{
static void Main(string[] args)
{
//Customers
var request1 = new LoanRequest() { Amount = 800, Customer = "Jimmy"};
var request2 = new LoanRequest() { Amount = 5000, Customer = "Ben"};
var request3 = new LoanRequest() {Amount = 200000, Customer = "Harry"};
//Approvers, chained together
var manager = new Manager() {Name = "Tom, Manager"};
var cashier = new Cashier(){ Name = "Job, Cachier", Successor = manager};
//All customers request cashier first to approve
cashier.HandleRequest(request1);
cashier.HandleRequest(request2);
cashier.HandleRequest(request3);
Console.ReadLine();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DesignPatternsCof
{
//Request
class LoanRequest
{
public string Customer { get; set; }
public decimal Amount { get; set; }
}
//Abstract Request Handler
interface IRequestHandler
{
string Name { get; set; }
void HandleRequest(LoanRequest req);
IRequestHandler Successor { get; set; }
}
//Just an extension method for the passing the request
static class RequestHandlerExtension
{
public static void TrySuccessor(this IRequestHandler current, LoanRequest req)
{
if (current.Successor != null)
{
Console.WriteLine("{0} Can't approve - Passing request to {1}", current.Name, current.Successor.Name);
current.Successor.HandleRequest(req);
}
else
{
Console.WriteLine("Amount invaid, no approval given");
}
}
}
//Concrete Request Handler - Cachier
//Cachier can approve requests upto 1000$$
class Cashier : IRequestHandler
{
public string Name { get; set; }
public void HandleRequest(LoanRequest req)
{
Console.WriteLine("\n----\n{0} $$ Loan Requested by {1}",
req.Amount, req.Customer);
if (req.Amount<1000)
Console.WriteLine("{0} $$ Loan approved for {1} - Approved by {2}",
req.Amount,req.Customer, this.Name);
else
this.TrySuccessor(req);
}
public IRequestHandler Successor { get; set; }
}
//Concrete Request Handler - Manager
//Manager can approve requests upto 10000$
class Manager : IRequestHandler
{
public string Name { get; set; }
public void HandleRequest(LoanRequest req)
{
if (req.Amount < 10000)
Console.WriteLine("{0} $$ Loan approved for {1} - Approved by {2}",
req.Amount, req.Customer, this.Name);
else
this.TrySuccessor(req);
}
public IRequestHandler Successor { get; set; }
}
//Main driver
class Program
{
static void Main(string[] args)
{
//Customers
var request1 = new LoanRequest() { Amount = 800, Customer = "Jimmy"};
var request2 = new LoanRequest() { Amount = 5000, Customer = "Ben"};
var request3 = new LoanRequest() {Amount = 200000, Customer = "Harry"};
//Approvers, chained together
var manager = new Manager() {Name = "Tom, Manager"};
var cashier = new Cashier(){ Name = "Job, Cachier", Successor = manager};
//All customers request cashier first to approve
cashier.HandleRequest(request1);
cashier.HandleRequest(request2);
cashier.HandleRequest(request3);
Console.ReadLine();
}
}
}
And this is what you’ll see upon execution.
So, you may observe that Loan Requests from different customers are
passed to the cashier in the above example, and the cashier in his
approve method passes the request to his successor (i.e, the manager) if
the amount is higher than what he can approve. The implementation is
pretty minimal, as you can see.
We actually have an Abstract request handler implementation IReqeustHandler
and two concrete request handlers, Cashier and Manager. Each request
handler may hold a reference to the successor. You may see that we are
setting the Successor of Cashier as Manager, so if the amount requested
his beyond a limit, the cashier may pass it to the manager for his
approval.
Reference:
No comments:
Post a Comment