Saturday, September 5, 2009

Patterns - 025: Structural patterns ( chain of responsibility )



When there is more than one object that can handle or fulfill a client request, the CoR pattern recommends giving each of these objects a chance to process the request in some sequential order. Applying the CoR pattern in such a case, each of these potential handler objects can be arranged in the form of a chain, with each object having a pointer to the next object in the chain.

The first object in the chain receives the request and decides either to handle the request or to pass it on to the next object in the chain.

The request flows through all objects in the chain one after the other until the request is handled by one of the handlers in the chain or the request reaches the end of the chain without getting processed.

The following are some of the important characteristics of the CoR pattern:
  • The set of potential request handler objects and the order in which these objects form the chain can be decided dynamically at runtime by the client depending on the current state of the application.
  • A client can have different sets of handler objects for different types of requests depending on its current state. Also, a given handler object may need to pass on an incoming request to different other handler objects depending on the request type and the state of the client application. In Java this can be accomplished by having different handlers implement a common interface or be subclasses of a common abstract parent class.
  • The client object that initiates the request or any of the potential handler objects that forward the request do not have to know about the capabilities of the object receiving the request. This means that neither the client object nor any of the handler objects in the chain need to know which object will actually fulfill the request.
  • Request handling is not guaranteed. This means that the request may reach the end of the chain without being fulfilled.

Example

Let us consider an application to simulate the purchase request (PR) authorization process in a typical organization. Let us consider an organization with four levels of management personnel
listed in the Table who can authorize a PR with an amount less than their authorization limit.


Management Level Authorization Limit

Branch Manager

$25,000

Regional Director

$100,000

Vice President

$200,000

President and COO

$400,000


Applying the CoR pattern, let us define an abstract PRHandler class that declares the common interface to be offered by all of the potential PR handlers.














            public abstract class PRHandler {
              private PRHandler nextHandler;
              private String handlerName;

              public PRHandler(String name) {
                handlerName = name;
              }
              public String getName() {
                return handlerName;
              }
              public abstract boolean authorize(PurchaseRequest request);
              public PRHandler getNextHandler() {
                return nextHandler;
              }
              public void setNextHandler(PRHandler handler) {
                nextHandler = handler;
              };
            }

            class BranchManager extends PRHandler {
              static double LIMIT = 25000;

              public BranchManager(String name) {
                super(name);
              }
              public boolean authorize(PurchaseRequest request) {
                double amount = request.getAmount();

                if (amount <= LIMIT) {
                  System.out.println(" Branch Manager " + getName() +
                                     " has authorized the PR - " + request);
                  return true;
                } else {
                  //forward the request to the next handler
                  return getNextHandler().authorize(request);
                }
              }

            }

            class RegionalDirector extends PRHandler {
              static double LIMIT = 100000;

              public RegionalDirector(String name) {
                super(name);
              }

              public boolean authorize(PurchaseRequest request) {
                double amount = request.getAmount();

                if (amount <= LIMIT) {
                  System.out.println(" Regional Director " + getName() +
                                     " has authorized the PR - " +
                                     request);
                  return true;
                } else {
                  //forward the request to the next handler
                  return getNextHandler().authorize(request);
                }
              }

            }

            class VicePresident extends PRHandler {
              static double LIMIT = 200000;

              public VicePresident(String name) {
                super(name);
              }

              public boolean authorize(PurchaseRequest request) {
                double amount = request.getAmount();

                if (amount <= LIMIT) {
                  System.out.println(" V.P. " + getName() +
                                     " has authorized the PR - " + request);
                  return true;
                } else {
                  //forward the request to the next handler
                  return getNextHandler().authorize(request);
                }
              }

            }

            class PresidentCOO extends PRHandler {
              static double LIMIT = 400000;

              public PresidentCOO(String name) {
                super(name);
              }

              public boolean authorize(PurchaseRequest request) {
                double amount = request.getAmount();

                if (amount <= LIMIT) {
                  System.out.println(" President & COO " + getName() +
                                     " has authorized the PR - " + request);
                  return true;
                } else {
                  System.out.println("PR - " + request +
                                     " couldn't be authorized.\n " +
                                     "Executive Board needs to be " +
                                     "consulted for approval \n" +
                                     "reason: Amount too large");
                  return false;
                }
              }
            }

            public class PRManager {

              private BranchManager branchManager;
              private RegionalDirector regionalDirector;
              private VicePresident vicePresident;
              private PresidentCOO coo;

              public static void main(String[] args) {
                PRManager manager = new PRManager();
                manager.createAuthorizationFlow();

                PurchaseRequest request =
                  new PurchaseRequest(1, "Office Supplies",10000);
                manager.branchManager.authorize(request);

                request = new PurchaseRequest(2, "HardWare Procurement",
                          175000);
                manager.branchManager.authorize(request);
                request = new PurchaseRequest(3, "AD Campaign",800000);
                manager.branchManager.authorize(request);

              }

              public void createAuthorizationFlow() {
                branchManager = new BranchManager("Robin");
                regionalDirector = new RegionalDirector("Oscar");
                vicePresident = new VicePresident("Kate");
                coo = new PresidentCOO("Drew");

                branchManager.setNextHandler(regionalDirector);
                regionalDirector.setNextHandler(vicePresident);
                vicePresident.setNextHandler(coo);
              }

            }

            class PurchaseRequest {

              private int ID;
              private String description;
              private double amount;

              public PurchaseRequest(int id, String desc, double amt) {
                ID = id;
                description = desc;
                amount = amt;
              }

              public double getAmount() {
                return amount;
              }

              public String toString() {
                return ID + ":" + description;
              }

            }

No comments:

Post a Comment