SHOEISHA iD

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

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

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

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

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

 次は、利用者が本の取り置きを依頼できる状況について考えてみます。本が返却された場合、図書館員は取り置き本をカウンターの奥に置きます。別の利用者がその本を借りようとしても拒否されます。対象の本が貸し出されていた場合、システムはその返却時に本を取り置き中としてマークします。取り置きが有効なのは3日間だけです。更新ルーチンが毎日実行されてすべての取り置き情報を調べ、有効期限が切れたものをクリアします。3日間のタイマーは、本が貸し出されている場合、返却時に1回だけ開始されます。既に取り置き中の本に、取り置き依頼を入れることはできません。

 これらの要件に基づき、利用者の取り置き状況を管理するために作成したコードがリスト3です。それほど悪くはないのですが、どこで何をしているのかだんだんわからなくなってきました。例えば、取り置き情報を更新するときにはif文を正しい場所に置かなければなりません。もしも、取り置きをクリアする必要があるかどうかを調べるコードを追加するときに、そのHoldingがもう取り置き中でないというケースに対する配慮を忘れてしまうと、NullPointerExceptionがスローされます。本の転送や予約の設定など、同様の機能について考えていくと、コードはあっという間に手に負えなくなり、混乱をきたすことでしょう。

リスト3 修正後のHolding
// HoldingTest.java
import static org.junit.Assert.*;
import java.util.*;
import org.junit.*;

public class HoldingTest {
   private Holding holding;
   private static final Date NOW = new Date();
   private static final Date LATER = new Date(NOW.getTime() + 1);
   private static final String PATRON_ID1 = "12345";
   private static final String PATRON_ID2 = "22345";

   @Before
   public void initialize() {
      Book book = BookTest.CATCH22;
      int copyNumber = 1;
      holding = new Holding(book, copyNumber);
   }

   @Test
   public void create() {
      assertSame(BookTest.CATCH22, holding.getBook());
      assertEquals(1, holding.getCopyNumber());
      assertFalse(holding.isOnLoan());
   }

   @Test
   public void checkout() {
      holding.checkout(NOW, PATRON_ID1);
      assertTrue(holding.isOnLoan());
      assertEquals(NOW, holding.getLoanDate());
   }

   @Test
   public void checkin() {
      holding.checkout(NOW, PATRON_ID1);
      holding.checkin(LATER);
      assertFalse(holding.isOnLoan());
   }

   @Test
   public void placeHoldOnCheckedInHolding() {
      assertFalse(holding.isOnHold());
      holding.placeHold(NOW, PATRON_ID1);
      assertTrue(holding.isOnHold());
   }

   @Test
   public void rejectsDuplicateHolds() {
      holding.placeHold(NOW, PATRON_ID1);
      try {
         holding.placeHold(NOW, PATRON_ID2);
         fail();
      } catch (HoldException expected) {
         assertTrue(holding.isOnHold());
      }
   }

   @Test(expected = HoldException.class)
   public void holdOnCheckedOutBookRetainedOnCheckin() {
      holding.checkout(NOW, PATRON_ID2);
      holding.placeHold(NOW, PATRON_ID1);
      holding.checkin(LATER);
      assertTrue(holding.isOnHold());
      holding.checkout(NOW, PATRON_ID2);
   }

   @Test
   public void releaseHold() {
      holding.placeHold(NOW, PATRON_ID1);
      holding.releaseAnyHold();
      assertFalse(holding.isOnHold());
   }

   @Test
   public void holdReleasedAfter3Days() {
      holding.placeHold(NOW, PATRON_ID1);
      holding.update(DateUtil.addDays(NOW, 1));
      assertTrue(holding.isOnHold());
      holding.update(DateUtil.addDays(NOW, 2));
      assertTrue(holding.isOnHold());
      holding.update(DateUtil.addDays(NOW, 3));
      assertFalse(holding.isOnHold());
   }

   @Test
   public void holdReleasedAfter3DaysAfterCheckin() {
      holding.checkout(NOW, PATRON_ID1);
      holding.placeHold(LATER, PATRON_ID1);
      Date checkinDate = DateUtil.addDays(NOW, 3);
      holding.update(checkinDate);
      assertTrue(holding.isOnHold());
      holding.checkin(checkinDate);
      assertTrue(holding.isOnHold());
      holding.update(DateUtil.addDays(checkinDate, 3));
      assertFalse(holding.isOnHold());
   }

   @Test
   public void releaseHoldHarmlessIfNoHolds() {
      holding.releaseAnyHold();
      assertFalse(holding.isOnHold());
   }

   @Test
   public void checkoutReleasesHold() {
      holding.placeHold(NOW, PATRON_ID1);
      holding.checkout(LATER, PATRON_ID1);
      assertFalse(holding.isOnHold());
   }

   @Test
   public void rejectCheckoutForHoldByDifferentPatron() {
      holding.placeHold(NOW, PATRON_ID1);
      try {
         holding.checkout(NOW, PATRON_ID2);
         fail();
      } catch (HoldException expected) {
      }
   }
}

// Holding.java
import java.util.*;

public class Holding {
   private final Book book;
   private final int copyNumber;
   private Date checkoutDate;
   private String holdPatron;
   private Date holdDate;

   public Holding(Book book, int copyNumber) {
      this.book = book;
      this.copyNumber = copyNumber;
   }

   public Book getBook() {
      return book;
   }

   public int getCopyNumber() {
      return copyNumber;
   }

   public boolean isOnLoan() {
      return checkoutDate != null;
   }

   public Date getLoanDate() {
      return checkoutDate;
   }

   public void checkout(Date date, String patronId) {
      if (isOnHold() && patronId != holdPatron)
         throw new HoldException();
      releaseAnyHold();
      checkoutDate = date;
   }

   public void checkin(Date date) {
      checkoutDate = null;
   }

   public void placeHold(Date date, String patronId) {
      if (isOnHold())
         throw new HoldException();
      this.holdPatron = patronId;
      holdDate = new Date();
   }

   public boolean isOnHold() {
      return holdPatron != null;
   }

   public void releaseAnyHold() {
      holdPatron = null;
   }

   public void update(Date date) {
      if (isOnHold() && !isOnLoan() &&
         DateUtil.daysBetween(holdDate, date) >= 3)
         releaseAnyHold();
   }
}

次のページ
状態の追跡

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

デザインパターンの使い方連載記事一覧

もっと読む

この記事の著者

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/3303 2008/12/12 14:00

イベント

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

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

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

メールバックナンバー