October 23, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Working With Design Patterns: Chain of Responsibility

  • May 7, 2008
  • By Jeff Langr
  • Send Email »
  • More Articles »

Listing 2: Approver.

public class Approver {
   private Approver nextApprover;
   private int dollarLimit;
   private boolean isOutOfOffice;
   private boolean canApproveInternational;
   private final String name;

   public Approver(String name, boolean canApproveInternational,
      int dollarLimit) {
      this.name = name;
      this.canApproveInternational = canApproveInternational;
      this.dollarLimit = dollarLimit;
   }

   public String getName() {
      return name;
   }

   public void handle(ExpenseReport report) {
      if (!canApprove(report) || isOutOfOffice())
         nextApprover.handle(report);
      else
         report.approve(this);
   }

   private boolean canApprove(ExpenseReport report) {
      return report.getTotalDollarAmount() <=
      getDollarLimit() &&
         (canApproveInternational() ||
          !report.isInternationalTravel());
   }

   public void setNextApprover(Approver approver) {
      nextApprover = approver;
   }

   public boolean isOutOfOffice() {
      return isOutOfOffice;
   }

   public void setOutOfOffice(boolean isOutOfOffice) {
      this.isOutOfOffice = isOutOfOffice;
   }

   public int getDollarLimit() {
      return dollarLimit;
   }

   public boolean canApproveInternational() {
      return canApproveInternational;
   }

   public void setCanApproveInternational(boolean
      canApproveInternational) { this.canApproveInternational =
      canApproveInternational;
   }

}

Listing 3: The Approver subtypes.

public class Manager extends Approver {
   private static final int APPROVAL_LIMIT = 5000;

   public Manager(String name, boolean canApproveInternational) {
      super(name, canApproveInternational, APPROVAL_LIMIT);
   }
}

public class VicePresident extends Approver {
   private static final int APPROVAL_LIMIT = 100000;

   public VicePresident(String name) {
      super(name, true, APPROVAL_LIMIT);
   }
}

public class CEO extends Approver {
   private static final int APPROVAL_LIMIT = Integer.MAX_VALUE;

   public CEO(String name) {
      super(name, true, APPROVAL_LIMIT);
      setNextApprover(new AutoRejectHandler());
   }
}

public class AutoRejectHandler extends Approver {
   public AutoRejectHandler() {
      super("auto-reject", false, 0);
   }

   @Override
   public void handle(ExpenseReport report) {
      report.reject();
   }

}

I've included JUnit tests (Listing 4) to step through the various scenarios. These unit tests take advantage of the ability of JUnit 4.x to allow multiple "setup" (@Before) methods. One such @Before method, createReport, initializes an expense report; one verifies that the test amounts used are in line with the actual limits expressed in the code (amountPreconditions); and one creates a default responsibility chain with a manager, VP, and CEO object.

The final test, approvedByPeerWhenManagerOutOfOffice, demonstrates one key feature of an appropriate chain of responsibility implementation: the ability for the chain to be dynamically altered. In this test scenario, the first manager (Joe) knows he will be out of the office. He arranges for all incoming expense reports to be routed to his peer manager, Jim. Jim reports to the same VP as Joe, and thus sets that VP as the next approver if Jim in turn cannot handle the expense report.





Page 2 of 3



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel