March 8, 2021
Hot Topics:

Working With Design Patterns: Chain of Responsibility

  • 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())

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

   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 =


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

   public void handle(ExpenseReport report) {


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

This article was originally published on May 7, 2008

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date