- PR -

複数のリスナーを登録してイベントを発生させたい

投稿者投稿内容
未記入
大ベテラン
会議室デビュー日: 2008/02/07
投稿数: 115
投稿日時: 2008-12-21 21:30
あなたがやろうとしていることが良く分かりません。クラスの名前が EventManager となっていますが、これは何です? Foo1Occurred イベント、Foo2Occurred2 イベントを持つイベントソースではないのですかね? それとも他のイベントソースの実装を代理するような存在としてクラスを設計しているのでしょうか?

コード:
EventManager em = new EventManager();
em.fireKeyPressed(new KeyEventPressed()); // KeyListener.pressed メソッドを呼び出す
em.fireMousePressed(new MouseEventPressed()); // MouseListener.pressed メソッドを呼び出す
em.fireMouseReleased(new MouseEventReleased()); // MouseListener.released メソッドを呼び出す



まず、あなたの書いている上記のコードが私には理解できません。イベントとは何かの操作や変化に対して発生するものですから、fire メソッドを外部から呼び出すという上記のコード自体が、イベント設計として何か勘違いしているのではないかという印象を受けます。

あなたが最初に提示したコードでは fire メソッドは protected になっていますよね? これはイベント設計としては適切なアクセス範囲だと私は思っています。あなたは、なぜ、fire メソッドを protected にしたのですか? そして、なぜ、ここで em.fire〜 という外部からイベント発行を呼び出すコードを提示されたのですか?

EventManager クラスとは何ですか? イベントを管理するクラスが何のために必要になりましたか? 通常は、それぞれのクラス(イベントソースとなるオブジェクト)について、イベントを実装(add/remove/get/fire)すれば済みます。そうではなくて、イベント関連メソッド(add/remove/get/fire)を、他のクラス(EventManager) に委譲するような設計を考えているのでしょうか?

あしゅ
ぬし
会議室デビュー日: 2005/08/05
投稿数: 613
投稿日時: 2008-12-22 10:09
taziさんが悩んでいるところって、

イベントを通知する側のクラスがいくつもあるのだが、それぞれのクラスで個別に
リスナのリストを管理すると、個々の通知側のクラスにコードが分散してしまう。

そこで、まとめて扱うためのEventManagerを導入しようと考えたのだが、
複数の種類のイベントを扱おうとしたときに、どう設計したらよいか
わからなくなった、というところなのではないですか?

そうだとすると、

java.beans.PropertyChangeSupportの考え方が参考になると思います。
これはリスナのリスト管理とリスト中のリスナへのイベント発火を行うクラスで、
taziさんのEventManagerと似ていますが、対象はPropertyChangeEventのみです。

ひとつのクラスで全ての種類のイベントに対応しようとすると
対応したいイベントの種類が増える度に際限なく肥大化していきますが、
個々のサポートクラスの責任範囲をひとつのイベントの種類に限定すれば、
サポートクラスの数は増えますが、個々のクラスはシンプルなままです。
tazi
会議室デビュー日: 2006/06/17
投稿数: 11
投稿日時: 2008-12-27 19:40
返答が遅くなり申し訳ございません。
EventManager クラスの役割についてですが、あしゅさんがフォローして頂いた通りです。

現在書いているプログラムはそれほど大規模なものではないため、EventManager クラスに全てのイベントを管理させる形で特に問題なく実装できました。
ただし、今後のためにもう少し勉強したいと思います。
皆さん色々とアドバイスありがとうございました。
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2008-12-28 17:46
みなさまこんにちは。
以下はとくにどなたへのコメントというわけではなく、私のメモ書きだと思って見ていただければと思います。

こういうイベントマネージャーのような仕組みは、(Java ではないですが)たとえば Windows 3.0 や 2.0 の時代からも SendMessage をしてそれを WndProc(HWND, UINT, WPARAM, LPARAM) のような仕組みで受けていました。このキューに溜まるメッセージはさまざまな種類のメッセージのごった煮状態であり、使用言語はオブジェクト指向言語に限定するわけではありませんが、受け取った側でダウンキャスト(や instanceof などでの判定)に相当することをやることは必要でした。
そして、こういうイベントマネージャーはだれが作ってもこれと似たような仕組みになってしまうものだと思います。

この理由は、結局は、キーやマウスといった種類の違うメッセージをひとつのキューに入れるからであり、仕方がないことだと思います。そしてなぜキューを種類ごとに分けずにひとつにするのかというと、種類数の数だけ爆発的に増えるのを避けることと、また、もうひとつの理由としては時系列で管理したいから、ということもあるかなと思います。

私は、種類の数がそれほど増えないのならば、キューは種類の数だけ分けるのもアリかなと思います。時系列の管理がややこしくはなりますが、それほど難しくはないでしょう。
sawat
大ベテラン
会議室デビュー日: 2006/08/02
投稿数: 112
投稿日時: 2009-01-09 17:02
去年のネタですが、面白そうなのでジェネリクスを使って書いてみた。

以下がtazi氏のEventManager相当のクラス。
コード:

package sample;
import java.util.EventListener;
import javax.swing.event.EventListenerList;

public class EventMulticaster {
public interface IEvent {}
public interface EventDispatcher<T extends EventListener, E extends IEvent> {
void dispatch(T listener, E event);
}

private EventListenerList listenerList = new EventListenerList();

public EventMulticaster() {}

public <T extends EventListener> void addEventListener(
Class<T> listenerTypeToken, T listener) {
listenerList.add(listenerTypeToken, listener);
}

public <T extends EventListener> void removeEventListener(
Class<T> listenerTypeToken, T listener) {
listenerList.remove(listenerTypeToken, listener);
}

public <T extends EventListener, E extends IEvent> void fire(
Class<T> listenerTypeToken, E event, EventDispatcher<T, E> dispatcher) {
for (T listener : listenerList.getListeners(listenerTypeToken)) {
dispatcher.dispatch(listener, event);
}
}
}




以下がそれを使うクラス。
コード:

package sample;
import java.util.EventListener;
import sample.EventMulticaster.IEvent;
import sample.EventMulticaster.EventDispatcher;

public class EventMulitcasterTest {
static class FooKeyEvent implements IEvent {}
static class FooMouseEvent implements IEvent {}

interface FooMouseEventListener extends EventListener {
void clicked(FooMouseEvent e);
}
interface FooKeyEventListener extends EventListener {
void pressed(FooKeyEvent e);
}

public static void main(String[] args) {
EventMulticaster em = new EventMulticaster();
// リスナの登録
em.addEventListener(FooMouseEventListener.class,
new FooMouseEventListener() {
public void clicked(FooMouseEvent e) {
System.out.println("Clicked!");
}
});
em.addEventListener(FooKeyEventListener.class,
new FooKeyEventListener() {
public void pressed(FooKeyEvent e) {
System.out.println("Pressed!!");
}
});
em.addEventListener(FooKeyEventListener.class,
new FooKeyEventListener() {
public void pressed(FooKeyEvent e) {
System.out.println("Pressed!!!");
}
});

// EventDispatcherを作る。
EventDispatcher<FooKeyEventListener, FooKeyEvent> fooKeyEventDispatcher
= new EventDispatcher<FooKeyEventListener, FooKeyEvent>() {
public void dispatch(FooKeyEventListener listener, FooKeyEvent event) {
listener.pressed(event);
}
};
EventDispatcher<FooMouseEventListener, FooMouseEvent> fooMouseEventDispacher
= new EventDispatcher<FooMouseEventListener, FooMouseEvent>() {
public void dispatch(FooMouseEventListener listenr, FooMouseEvent event) {
listenr.clicked(event);
}
};

// イベントを発生させる。
em.fire(FooKeyEventListener.class, new FooKeyEvent(), fooKeyEventDispatcher);
em.fire(FooMouseEventListener.class, new FooMouseEvent(), fooMouseEventDispacher);
em.fire(FooKeyEventListener.class, new FooKeyEvent(), fooKeyEventDispatcher);
em.fire(FooMouseEventListener.class, new FooMouseEvent(), fooMouseEventDispacher);
}
}



EventListenerListを扱う処理がEventMulitcasterクラス内に隠ぺいされました。
個々のイベントソースでEventListenerListを直接扱わずに済むので、
ちょっとは利用価値があるのかな。
メソッド呼び出しにいちいち型トークン(Hoge.class)を指定するのが面倒ですが。

注意 (追記)
上記のEventMulticasterではイベントオブジェクトをすべての配信先リスナで共有しています。安全に共有するためにはイベントオブジェクトは不変オブジェクトであるべきです。
EventMulticasterは独自のIEvent型を使用していますが、それをjava.awt.AWTEventなどに変える場合は、AWTEventは不変でないのでEventDispatcherのdispatchメソッドの内部でイベントを生成する必要があるでしょう。EventListenerListのドキュメントも参考にしてください。


[ メッセージ編集済み 編集者: sawat 編集日時 2009-01-09 17:45 ]

スキルアップ/キャリアアップ(JOB@IT)