x09. デザインパターン(1/3)
演習の準備:
プロジェクト名 Project5
先回の課題 L7_1.java を L8_1.javaとしてベースに利用する。 コンソールアプリケーションから、JavaApplet として動作するように修正。
init メソッドから、 main メソッドを呼び出すようにしておく。
paint メソッドを追加する。
※ eclipse の機能を活用して修正する。 エラー自動修正、 (メニュー)ソース → メソッドの実装 からスーパークラスのメソッドを選択して実装。
課題:
・Animal の name フィールドを private に設定し、 setName(String name) など、アクセサー・メソッド を利用する様に修正する。
ソース → getter / setter の設定から、 生成。 public メソッドを指定。
name フィールドに直接値を代入することを避ける。 nameの仕様変更(型が変わるなど)の場合でも、対応可能なようにしておく。
setterの利用例)
名前の付け方の規則を強制する。
public void setName(String name) {
this.name = name + "=サン";
}
・猫が複数のアイテムを持てるようにCatクラスを再設計
+
・Itemの使い方を、Itemの種類に応じて変えられるように Item クラスを作成
+
・Catクラスに ItemクラスのListを作成し、幾つかの要素を持たせる。拡張 for で全要素に同じアイテムを持たせ、使用させ、捨てさせる。
Catクラスに追加
List<Item> items;
Catクラスの items フィールドを ArrayList で初期化。
List はインターフェースなので new List() の様にインスタンス化できない。
List を実装した、ArrayList で初期化する。
List を実装していれば、ArrayList以外のオブジェクトで初期化してもよい。
List<Item> items = new ArrayList<Item>();
Itemクラスの追加
interface ItemOwner {
void attachItem(String item);
void attachItem(Item item);
void dropItem();
void useItem();
}
abstract class Item {
String name;
abstract void use();
}
Appletに、inner-class として Item のサブクラス Bell を追加し。サウンドを再生できるようにする。
class Bell extends Item {
AudioClip se;
Bell(AudioClip se) {
this.se = se;
}
void use() {
se.play();
}
}
init メソッドから Bell のサウンドの初期化を行う。
ベルの再生音を用意する( wav ファイル)。ネットから検索してダウンロードするか、レポートフォルダ(オブジェクト指向)に用意したものを利用してよい。
wavファイルは、 プロジェクトの src フォルダに配置する。
アプレットで wav を読み込み、変数を初期化する。
public class L8_1 extends Applet {
AudioClip s;
wav の読み込みと、 Bellオブジェクトの初期化。
ついでに、mainの利用をコメントアウト。
新たに猫を用意し、鈴を取り付け、アイテムを使用させる。
public void init() {
//main(null);
s = getAudioClip(getCodeBase(),"nc52380.wav");
Cat mycat = new Cat();
mycat.attachItem(new Bell(s));
mycat.useItem();
}
Catクラスに ItemOwner インターフェースを実装し、Itemを所有し、使用できるように修正する。
class Cat extends Animal implements ItemOwner
Item オブジェクトを装備できるようにメソッドを追加。
@Override
public void attachItem(Item item) {
items.add(item);
}
装備した Item をすべて使用するように、 useItem メソッドを修正。
public void useItem() {
for (Item item : items) {
item.use();
System.out.printf("%s は %s を使いました%n", getName(), item.name);
}
}
上のコードで Catの name フィールドへのアクセサメソッド getName() を使用しているので、新規定義する。
public String getName() {
return this.name;
}
応用課題:
取り付けるアイテムを増やして、いろいろ試してみよう。
まずは、Bellを3個付けるなど。
Bell の他に アイテムオブジェクトを設計してみよう。
例)
弾数6の銃 残弾がないときは、発射音なし。
class Gun extends Item {
int ammo = 6;
AudioClip se;
Gun(AudioClip se) {
this.se = se;
}
void use() {
if(ammo >0) {
se.play();
ammo--;
}
}
}
問題点
猫の行動の表現を、 コンソール上にメッセージで表示している場合は、メッセージは順番に表示されるので、実行した順番 は判別可能。
しかし、音声の再生や、画面の描画は、コンソールのメッセージ表示とは 別のスレッド で並行して実行されるので、
順序が判別できない。
useItem メソッドによる音声ファイルの再生は、同時に処理され、各音声は一斉に再生される。
同様に、グラフィカルな表現を猫に与えて Applet 上で行った場合でも、図形の描画コマンドは一斉に処理されるので、描画順序は判別できない。
解決:
手早い方法としては、http://d.hatena.ne.jp/konchi/20090911/1252646115
各 item を処理するループや、Cat にメソッドを適用するたびに、 Thread.sleep(1000) でメインのスレッドを 1000ms 程度一時停止し、処理順序の区別が付くようにする。
※スレッドを停止する時間は、再生される音声の長さに合わせて調整する必要がある。
例)
for(Item item:items) {
item.use();
System.out.printf("%s は %s を使いました%n",getName(),item.name);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
将来的には、Cat の行動処理について、 アニメーション付き、 アニメーションなし を選べるように、クラス再設計を検討。