CardDealerアプリケーションを理解する
RESTとJerseyの使用方法に関する最も基本的な部分を理解したところで、今度は、実際にプログラミングをしてみましょう。上述のとおり、ランダムにトランプのカードを1枚取り出し、それをブラウザにグラフィカルに表示する、CardDealerというWebアプリケーションを作成します。
CardDealerアプリケーションは、3つの要素で構成されています。トランプのカードを表すJSON形式のオブジェクトを返すWebベースのRESTレイヤ、論理的なカードディーラーコンポーネントを提供するサービスレイヤ(ShoeFactory、Shoe、Card、CardDealerHelper)、そして3つめは、トランプの各カードのグラフィカル表現を提供するファイルシステムベースのファイル集合です。
RESTレイヤは、RESTfulなURLに関連付けられたいくつかのエンドポイントを公開します。これらのエンドポイントは、RESTリソースクラスとして機能するJavaクラス内のメソッドに対応しています。RESTリソースクラスは、CardDealerRestという名前です。CardDealerRestのメソッドは、上述のサービスレイヤのクラスであるShoeFactory、Shoe、Card、CardDealerHelperに存在するカード処理機能を利用します。
Shoeオブジェクトは、カジノで見られるカードシューを論理的に表現したものです。従ってShoeオブジェクトには、1つまたは複数のカードデッキが含まれます。Shoeオブジェクトで公開されるメソッドは、実際のカードシューに対応しており、例えば、Shoe.getCard()、Shoe.shuffle()、Shoe.getDeckCount()、Shoe.setDeckCount()、Shoe.GetCardCodes()などがあります。
Shoeオブジェクトには、ShoeFactoryオブジェクトからアクセスします。ShoeFactoryはシングルトンです。ShoeFactoryは、少なくとも1つ、または複数のShoeオブジェクトを管理します。ShoeFactory.getShoe()を呼び出すことにより、ShoeFactoryに対して新しいShoeを要求します。特定のShoeを取得するには、ShoeFactory.getShoe(String shoeId)を呼び出します。ここでshoeIdは、Shoeオブジェクトの識別子です。
カードデッキはShoeの中で、StringオブジェクトのArrayListとして表されています。各Stringは、一意に識別可能なカードコードです。カードコードは、CardDealerアプリケーション独自の特定の命名規則に従っています。命名規則は、[suit_code][rank_number]です。ここで、suit_codeはトランプの絵柄を表し、以下のうちのいずれかとなります。
- s:スペード
- c:クラブ
- h:ハート
- d:ダイヤ
rank-numberでは、エースを最も小さい数値である1に、キングを最も大きな数値である13に割り当てます。つまり、ダイヤの5は「d5」、スペードのエースは「s1」、クラブのジャックは「c11」となります。
アプリケーションでは、トランプの52枚のカードの画像を含むディレクトリが必要です。各カードのファイル名は、カードコードに対応しています。画像としては、jpg、gif、pngなど、ブラウザで一般的にサポートされている任意の形式を使用できます。例えば、クラブの6に対応する画像のファイル名は、「c6.jpg」、「c6.gif」、または「c6.png」となります。ハートの2に対応する画像のファイル名は、「h2.jpg」、「h2.gif」、または「h2.png」となります。繰り返しになりますが、画像ファイル名はカードコードに対応していなければなりません。ファイル拡張子の種類は関係ありません。ただし、Cardオブジェクトは、画像ファイルを調べて、ファイル拡張子を確認します。ファイル名の拡張子は、Cardオブジェクトの1つのフィールドとして表されます。このフィールドの名前はfileFormatです。
トランプカードの画像を含むディレクトリの場所は、CardDealerHelperオブジェクトのimageDirectoryLocationFileSpecフィールドで定義されます。CardDealerHelperは、サービスレイヤライブラリの1つです。アプリケーションでは、Springの実行時インジェクションを用いて、CardDealerHelper.imgDirectoryLocationFileSpecの値を設定します。
Shoeの生成時には、imgDirectoryLocationFileSpecの値が定義されていることと、52枚のトランプの画像ファイルが、それぞれのカードコードに対応するファイル名でimgDirectoryLocationFileSpecディレクトリに存在することを確認します。
サービスレイヤは、InvalidCardDeckExceptionとEmptyShoeExceptionという2つの例外を発行します。InvalidCardDeckExceptionは、imgDirectoryLocationFileSpecで定義されるディレクトリに足りないカードがあることを報告する例外です。EmptyShoeExceptionは、Shoeに対してカードが要求されたときに、Shoeにカードが1枚も残っていない場合に発生します。
Shoeでは、Shoe.getEmptyShoeCard()というメソッドが公開されています。Shoe.getEmptyShoeCard()は、空のカードシューを表す画像を持つCardオブジェクトを返します。空のカード画像は、どのようなものでもかまいません。サービスレイヤでは、imgDirectoryLocationFileSpecが示すディレクトリに、空のカード画像のファイルが格納されているとみなします。空のカード画像のファイル名は、CardDealerHelper.emptyShoeImageFileSpecフィールドに格納されています。CardDealerHelper.emptyShoeImageFileSpecの値は、Springのインジェクションによって実行時に設定されます。
カードを完全にランダムに表示するために、Cardオブジェクトは、画像ファイルの物理的な内容であるimageBytesフィールドを公開します。Cardオブジェクトのコンストラクタには、対応するカードコードを表すcardCodeという引数があります(リスト2を参照ください)。コンストラクタでは、引数cardCodeに基づいてファイルシステムから画像ファイルを取り出します。Cardオブジェクトのコンストラクタでは、そのカード画像をCardオブジェクトのimageBytesフィールドにバイト配列として代入します。また、画像ファイルの形式/拡張子をCardオブジェクトのfileFormatフィールドに代入します。カードコードは、Cardオブジェクトのcodeフィールドに代入されます。これにより、Cardオブジェクトを受け取った側では、使用されているファイル名は分かりませんが、カードの画像を表示し、カードの絵柄と番号を知ることができます。
01: /**
02: * Instantiates a new card.
03: *
04: * @param cardCode the card code
05: * @param shoeId the shoe id
06: *
07: * @throws IOException Signals that an I/O exception has occurred.
08: */
09: public Card(String cardCode, String shoeId) throws IOException {
10: code = cardCode;
11: imagesDirectoryLocation = CardDealerHelper.getImageDirectoryLocation();
12: String[] files = CardDealerHelper.getImagesDirectoryFileNames();
13: //Go through all the files in the image directory and find
14: //the one that matches the card code. Once found, process accordingly
15: for (int i = 0; i < files.length; i++) {
16: if (files[i].toLowerCase().indexOf(cardCode.toLowerCase()) != -1) {
17: this.shoeId = shoeId;
18: this.imageUrl = imagesDirectoryLocation + "/" + files[i];
19: this.imagesBytes = CardDealerHelper
20: .getImageBytesFromFileSpec(this.imageUrl);
21: this.fileFormat = files[i].substring(
22: files[i].lastIndexOf('.') + 1).toLowerCase();
23: }
24: }
25:
26: if (this.imagesBytes == null) {
27: throw new IllegalArgumentException("Card image bytes is empty");
28: }
29: }
また、ユーザーが別のトランプ画像を使用したい場合には、別のディレクトリに異なるカード画像一式を格納し、CardDealerHelperオブジェクトのsetImageDirectoryFileSpec()メソッドを使用して、別のディレクトリにある新しいカード画像を、アプリケーションに実行時に参照させることができます。
CardDealerの構成を図3に示します。
図4は、CardDealerコンポーネント間の制御フローを表す、非公式なシーケンス図です。


