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 の行動処理について、 アニメーション付き、 アニメーションなし を選べるように、クラス再設計を検討。