非常に一般的に、特定のオブジェクトに多くのリスナーが必要になる状況があります。たとえば、私は持っているかもしれません
class Elephant {
public void addListener( ElephantListener listener ) { ... }
}
しかし、私にはそのような状況がたくさんあります。つまり、Tiger
sを持つTigerListener
オブジェクトもあります。現在、TigerListener
sとElephantListener
sはまったく異なります。
interface TigerListener {
void listenForGrowl( Growl qrowl );
void listenForMeow( Meow meow );
}
ながら
interface ElephantListener {
void listenForStomp( String location, double intensity );
}
私は常に各動物クラスでブロードキャストメカニズムを再実装し続けなければならないことに気づき、実装は常に同じです。推奨パターンはありますか?
各Listener
が送信できるすべてのイベントタイプに特定のメソッドを持っている代わりに、ジェネリックEvent
クラスを受け入れるようにインターフェイスを変更します。その後、必要に応じてEvent
を特定のサブタイプにサブクラス化するか、double intensity
などの状態を含めることができます。
TigerListenerとElephentListenerは、
interface TigerListener {
void listen(Event event);
}
実際、このインターフェイスをさらにListener
にさらにリファクタリングできます。
interface Listener {
void listen(Event event);
}
Listener
実装には、関心のある特定のイベントに必要なロジックを含めることができます
class TigerListener implements Listener {
@Overrides
void listen(Event event) {
if (event instanceof GrowlEvent) {
//handle growl...
}
else if (event instance of MeowEvent) {
//handle meow
}
//we don't care about any other types of Events
}
}
class ElephentListener {
@Overrides
void listen(Event event) {
if (event instanceof StompEvent) {
StompEvent stomp = (StompEvent) event;
if ("north".equals(stomp.getLocation()) && stomp.getDistance() > 10) {
...
}
}
}
}
サブスクライバーとパブリッシャーの間の重要な関係は、パブリッシャーがサブスクライバーにイベントを送信できるということです。必ずしも特定のタイプのイベントを送信できるとは限りません。このタイプのリファクタリングは、そのロジックをインターフェースから特定の実装にプッシュします。 。
これは、ここに来てリスナーを作りたいだけの人にとって、より一般的な答えです。 CodePathから Creating Custom Listeners を要約しています。詳細な説明が必要な場合は、その記事を読んでください。
手順は次のとおりです。
これは、不明な親と通信する必要がある子クラスにあります。
public class MyClass {
// interface
public interface MyClassListener {
// add whatever methods you need here
public void onSomeEvent(String title);
}
}
プライベートリスナーメンバー変数とパブリックセッターメソッドを子クラスに追加します。
public class MyClass {
// add a private listener variable
private MyClassListener mListener = null;
// provide a way for another class to set the listener
public void setMyClassListener(MyClassListener listener) {
this.mListener = listener;
}
// interface from Step 1
public interface MyClassListener {
public void onSomeEvent(String title);
}
}
子オブジェクトは、リスナーインターフェイスのメソッドを呼び出すことができます。聞いている人がいない可能性があるため、必ずnullを確認してください。 (つまり、親クラスがリスナーのセッターメソッドを呼び出していない可能性があります。)
public class MyClass {
public void someMethod() {
// ...
// use the listener in your code to fire some event
if (mListener != null)
mListener.onSomeEvent("hello");
}
// items from Steps 1 and 2
private MyClassListener mListener = null;
public void setMyClassListener(MyClassListener listener) {
this.mListener = listener;
}
public interface MyClassListener {
public void onSomeEvent(String myString);
}
}
これで、親が子クラスで設定したリスナーを使用できるようになります。
public class MyParentClass {
private void someMethod() {
MyClass object = new MyClass();
object.setMyClassListener(new MyClass.MyClassListener() {
@Override
public void onSomeEvent(String myString) {
// handle event
}
});
}
}
public class MyParentClass implements MyClass.MyClassListener {
public MyParentClass() {
MyClass object = new MyClass();
object.setMyClassListener(this);
}
@Override
public void onSomeEvent(String myString) {
// handle event
}
}
あなたのインターフェースは意味論的な価値を持ち、それらが聞いているものを表現しているので、あなたはそれを正しくやっていると思います(たとえば、踏み鳴る代わりにうなり声と鳴き声)。一般的なアプローチでは、ブロードキャストコードを再利用できますが、読みやすさが失われる可能性があります。
たとえば、値の変更をリッスンするOberserverを実装するためのユーティリティである_Java.beans.PropertyChangeSupport
_があります。ブロードキャストを行いますが、ドメインクラスにメソッドを実装し、PropertyChangeSupportオブジェクトに委任する必要があります。コールバックメソッド自体は意味がなく、ブロードキャストされるイベントは文字列ベースです。
_public interface PropertyChangeListener extends Java.util.EventListener {
void propertyChange(PropertyChangeEvent evt);
}
_
もう1つは、ブロードキャストメカニズムを提供する_Java.util.Observable
_ですが、それは私にとっても最高のものではありません。
ElephantListener.onStomp()
が好き
別のオプションは ホワイトボードパターン です。これにより、パブリッシャーとサブスクライバーの接続が切断され、どちらにもブロードキャストコードが含まれなくなります。どちらも単純にpub/subのメッセージングメカニズムを使用しており、どちらも他と直接接続していません。
これは、OSGiプラットフォームでのメッセージングの一般的なモデルです。