SHOEISHA iD

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

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

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

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

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

状態の追跡

 取り置き本(Holding)はさまざまな状態を取ると考えられ、例えば貸し出し中、返却済み、取り置き依頼あり、取り置き依頼なし、分館から分館への移動中などがあります。既存のコードの場合、Holdingは次の4つの状態のいずれかです。

  • 貸し出し中、取り置き依頼あり
  • 貸し出し中、取り置き依頼なし
  • 返却済み、取り置き依頼あり
  • 返却済み、取り置き依頼なし

 状態を定義することで、状態遷移図を作成できます(図1を参照)。状態遷移図では、個々の状態をボックスとして表現します。Holdingの返却など、特定のイベントが発生すると、Holdingは状態間を遷移します。通常は、別の状態に遷移する中でイベントによってアクションが呼び出されます。

図1 状態の遷移
図1 状態の遷移

 Stateパターンでは、こうした個々の状態を切り出し、それぞれを独立したクラスとして表現します。現在の状態は、Holdingオブジェクト内で、状態オブジェクトへの参照として追跡されます。今回の例では、起こり得るイベントをすべての状態に対するメソッドとして定義します。ただし各状態では、Holdingオブジェクトでアクションを呼び出すことにより、それぞれに適したイベントセットが処理されるようにしています。状態の遷移が必要な場合は、イベントハンドラでは、Holdingオブジェクトが持つ状態参照の更新も行います。

 テストを繰り返しながら、時間をかけてコードのリファクタリングをしていきましょう。作成する最初のクラスはHoldingStateクラスです(リスト4を参照)。これは、起こり得る各イベントに対してNULL動作を提供する抽象クラスです。

リスト4 HoldingState
// HoldingState
import java.util.*;

abstract class HoldingState {
   protected final Holding holding;
   HoldingState(Holding holding) {
      this.holding = holding;
   }
   void checkout(Date date, String patronId) {
   }
   void checkin(Date date) {
   }
   void placeHold(Date date, String patronId) {
   }
   void update(Date date) {
   }
}

 次に、このクラスに基づいて、4つの状態にそれぞれ対応する派生クラスを作成します。派生クラスを作成したら、Holdingクラスからコードを移動させます。各イベント(checkoutcheckinplaceHoldupdate)に対するHoldingオブジェクトの役割は、そのイベントを現在のHoldingStateオブジェクトにそのままデリゲートすることです。例えば、Holdingのcheckoutメソッドは、最終的には次のような形になります。

public void checkout(Date date, String patronId) {
   state.checkout(date, patronId);
}

 これらは単純なデリゲートなので、Javaプロキシのメカニズムを使うことにしましょう。現時点では、新たにHoldingに実装する必要が出てきた数多くのpublicインターフェースについては考慮しません。Holdingの拡張については後で行います。

 checkoutイベントに対して何らかの処理を行う必要がある状態オブジェクトは、アクションメソッドの呼び出しか、別の状態への遷移を行います(両方行う場合もあります)。状態オブジェクトの種類によっては、このイベントを無視することもあります。Stateパターンを実装すると、無視されていたイベントが見つかり、システムの潜在的な問題が明らかになることがあります。例えば、現時点のCheckedOut状態オブジェクトは、checkoutイベントに対して何も動作しません。この処理されないイベントのおかげで、誰かが同じ本を2回借りようとしたときにどう対処すべきかを決めなければならないことに気付きました。

 すべてのコードを状態オブジェクトに移動させる利点は、条件分岐が減ることです。CheckedInHeld状態のときに、処理を進める前に本が取り置きされているかどうかを確認する必要はもうありません。

class CheckedInHeld extends HoldingState {
   // ...
  @Override
   public void checkout(Date date, String patronId) {
      if (patronId != holding.holdPatron)
         throw new HoldException();
      holding.doCheckout(date, patronId);
      holding.state = new CheckedOut(holding);
   }
   // ...
}

 以前は、Holding内のcheckoutのコードで、本が既に取り置きされているかどうかを確認しなければなりませんでした。しかしコードを移動させたことにより、HoldingがCheckedInHeld状態を参照するときにこのコードが実行されるので、もう確認は必要ありません。

次のページ

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

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

もっと読む

この記事の著者

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」など、さまざまなカンファレンスを企画・運営しています。

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

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

メールバックナンバー