SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

デザインパターンの使い方

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

 さまざまなシナリオにおける動作を確認する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パターン

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
デザインパターンの使い方連載記事一覧

もっと読む

この記事の著者

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

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Jeff Langr(Jeff Langr)

本格的なソフトウェアの開発に四半世紀以上携わってきたベテランのソフトウェア開発者。『Agile Java: Crafting Code With Test-Driven Development』(Prentice Hall、2005年)と、他の1冊の著書がある。『Clean Code』(Uncle Bob Martin著、Prentice Hall、2008年8月)にも寄稿している。また、ソフトウェア開発に関する記事を80件以上執筆しており、そのうちの...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/3116 2008/11/10 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー