CodeZine(コードジン)

特集ページ一覧

デザインパターンの使い方: Chain of Responsibility

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2008/11/10 14:00

目次

 さまざまなシナリオにおける動作を確認するJUnitテスト(リスト4)を用意しました。これらの単体テストでは、複数の“setup”(@Before)メソッドを実行できる「JUnit 4.x」の機能を利用しています。この@Beforeメソッドとしては、経費報告書を初期化するcreateReport、テストで使用される金額とコードに記述された実際の制限値が一致することを確認するamountPreconditions、そして管理者(Manager)、副社長(VicePresident)、CEOをオブジェクトとして含むデフォルトの命令系統を作成するメソッドがあります。

 最終テストのapprovedByPeerWhenManagerOutOfOfficeでは、Chain of Responsibilityパターンの実装が備えるべき重要な機能の1つ、すなわち命令系統の動的な変更が可能かどうかが検証されます。このテストシナリオにおいて、最初の管理者(Joe)は自分が外出して不在になることを知っています。彼は自分の所に送られてくるすべての経費報告書が自分と同位の管理者(Jim)へ転送されるように手配します。JimはJoeと同じ副社長に報告を行うことになっているため、Jimの段階でも経費報告書を処理できない場合は、その副社長を次の承認者に設定します。

リスト4 JUnitテスト
import static org.junit.Assert.*;

import org.junit.*;

public class ChainTest {
   private Manager manager;
   private VicePresident vp;
   private CEO ceo;
   private ExpenseReport report;

   private static final int SMALL_AMOUNT = 100;
   private static final int VP_AMOUNT = 7000;
   private static final int CEO_AMOUNT = 207000;

   @Before
   public void amountPreconditions() {
      assertTrue(SMALL_AMOUNT <= manager.getDollarLimit());
      assertTrue(manager.getDollarLimit() < VP_AMOUNT &&
            VP_AMOUNT <= vp.getDollarLimit());
      assertTrue(vp.getDollarLimit() < CEO_AMOUNT);
   }

   @Before
   public void createReport() {
      report = new ExpenseReport();
   }

   @Before
   public void createDefaultChain() {
      manager = new Manager("Joe", false);
      vp = new VicePresident("Jane");
      ceo = new CEO("Zeus");

      manager.setNextApprover(vp);
      vp.setNextApprover(ceo);
   }

   @Test
   public void approvedByManager() {
      report.setTotalDollarAmount(SMALL_AMOUNT);
      manager.handle(report);
      assertApprovedBy(manager);
   }

   @Test
   public void approvedByVPWhenOverManagerLimit() {
      report.setTotalDollarAmount(VP_AMOUNT);
      manager.handle(report);
      assertApprovedBy(vp);
   }

   @Test
   public void approvedByCEOWhenOverVPLimit() {
      report.setTotalDollarAmount(CEO_AMOUNT);
      manager.handle(report);
      assertApprovedBy(ceo);
   }

   @Test
   public void approvedIntlByIntlManager() {
      report.setTotalDollarAmount(SMALL_AMOUNT);
      report.setIsInternationalTravel(true);
      manager.setCanApproveInternational(true);
      manager.handle(report);
      assertApprovedBy(manager);
   }

   @Test
   public void approvedIntlByVPWhenManagerNotIntl() {
      report.setTotalDollarAmount(SMALL_AMOUNT);
      report.setIsInternationalTravel(true);
      assertFalse(manager.canApproveInternational());
      manager.handle(report);
      assertApprovedBy(vp);
   }

   @Test
   public void approvedByVPWhenManagerOutOfOffice() {
      report.setTotalDollarAmount(SMALL_AMOUNT);
      manager.setOutOfOffice(true);
      manager.handle(report);
      assertApprovedBy(vp);
   }

   @Test
   public void rejectedWhenCEOOutOfOffice() {
      report.setTotalDollarAmount(CEO_AMOUNT);
      ceo.setOutOfOffice(true);
      manager.handle(report);
      assertSame(State.rejected, report.state());
   }

   @Test
   public void approvedByPeerWhenManagerOutOfOffice() {
      report.setTotalDollarAmount(SMALL_AMOUNT);
      Manager peer = new Manager("Jim", false);
      peer.setNextApprover(vp);
      manager.setOutOfOffice(true);
      manager.setNextApprover(peer);

      manager.handle(report);
      assertApprovedBy(peer);
   }

   private void assertApprovedBy(Approver approver) {
      assertSame(State.approved, report.state());
      assertSame(approver, report.getHandler());
   }
}

 Chain of Responsibilityパターンを使うクライアントは、どの承認者が最終的に経費報告書を処理することになるか知らず、実際そのことを気に掛けません。この実装において、経費報告書を提出する社員は最初の承認者(直属の上司)を知っています。しかし、クライアントがこうしたことまで知らなければならない理由はありません。クライアントに対して適切なApproverの実装を生成するようなファクトリを用意することも可能です。

図1 Chain of Responsibilityパターン
図1 Chain of Responsibilityパターン


  • LINEで送る
  • このエントリーをはてなブックマークに追加

バックナンバー

連載:デザインパターンの使い方

もっと読む

著者プロフィール

  • japan.internet.com(ジャパンインターネットコム)

    japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.com や EarthWeb.c...

  • Jeff Langr(Jeff Langr)

    本格的なソフトウェアの開発に四半世紀以上携わってきたベテランのソフトウェア開発者。『Agile Java: Crafting Code With Test-Driven Development』(Prentice Hall、2005年)と、他の1冊の著書がある。『Clean Code』(Uncle...

あなたにオススメ

All contents copyright © 2005-2022 Shoeisha Co., Ltd. All rights reserved. ver.1.5