Thursday, February 16, 2012

Design Pattern : Chain of Responsibility pattern Implementation

Definition

GoF -> A way of passing a request between a chain of objects
  1. 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.
  2. 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.
  3. 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.
  4. Sometimes, the request object has to go through the chain objects mandatorily, like Http pipeline.
  5. 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

chain1.gif

chain2.gif

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;
        }

        public abstract void HandleRequest();
    }
    ---------------- queue object one ----------------------
public class TeamOne: Handlerbase
    {
        public TeamOne(Handlerbase nextHandler, ContextObject question) : base(nextHandler, question) { }
        public override void HandleRequest()
        {
            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.....");
            NextTeam.HandleRequest();
        }
    }
    ---------------- chain object two ----------------------
    public class TeamTwo : Handlerbase
    {
        public TeamTwo(Handlerbase nextHandler, ContextObject question) : base(nextHandler, question) { }
        public override void HandleRequest()
        {
            Console.WriteLine("Wating for team two to respond");
            Thread.Sleep(10000);
            Console.WriteLine("\t no response from team two.....");
            NextTeam.HandleRequest();
        }
    }
    ---------------- chain object tree ----------------------
    public class TeamThree : Handlerbase
    {
        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 .....");
        }
    }
    ---------------- 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?" };
            TeamThree teamThree = new TeamThree(null, question);
            TeamTwo teamTwo = new TeamTwo(teamThree, question);
            TeamOne teamOne = new TeamOne(teamTwo, question);
            teamOne.HandleRequest();
            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();
        }


    }
And this is what you’ll see upon execution.
image
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.
image
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