Javaメッセージディスパッチャーの生物を避けます
-
26-10-2019 - |
質問
目的
私は、サードパーティのAPIからユーザー定義のメッセージにメッセージを変換し、ユーザー登録リスナーにディスパッチするMesagedispatcherを構築しようとしています。
ユーザーは次のことが期待されます。
- ユーザーメッセージの各タイプのインターフェイスを定義します。
- 各メッセージタイプのメッセージディスパッチャーにリスナーを登録します。
- RAW/サードパーティのデータをメッセージディスパッチャーに渡します。
- リスナーに渡されたメッセージを処理します。
問題の説明
残念ながら、生のタイプを使用して希望のAPIを達成することを避けることはできないようです。私は他の場所で、生のタイプを使用するための例外的なケースはなく、それらは後方互換性のために言語にのみ存在することを読みました。
以下のコードを変更して動作させる方法はありますか、それともAPIを再設計する必要がありますか?
インターフェイス
mesagedispatcherは、次のインターフェイスを実装しています。
public interface MessageDispatcher {
// Register a listener for a given user defined message type.
public <T> void registerListener(
Class<T> messageClass,
MessageListener<T> listener);
// Receive data in 3rd party format, convert and dispatch.
public void onData(Data data);
}
MessageListenerインターフェイスは、次のように定義されています。
public interface MessageListener<T> {
public void onMessage(T message);
}
ユーザーメッセージの例は、次のようになる場合があります。
public interface MyMessage {
public String getName();
}
リスナーの登録
ユーザーは次のようにリスナーを登録できます。
messageDispatcher.registerListener(MyMessage.class,
new MessageListener<MyMessage.class>() {
@Override
public void onMessage(MyMessage message) {
System.out.println("Hello " + message.getName());
}
}
標準のメッセージディスパッチャーは、次のような方法を実装する場合があります。
private Map<Class<?>,MessageListener<?>> messageClassToListenerMap;
public <T> void registerListener(
Class<T> messageClass,
MessageListener<T> listener) {
messageClassToListenerMap.put(messageClass, listener);
// SNIP: Process the messageClass and extract the information needed
// for creating dynamic proxies elsewhere in a proxy factory.
}
メッセージを発送します
Mesagedispatcherが新しいメッセージを受信すると、オブジェクトの動的プロキシを作成し、適切なリスナーにディスパッチします。しかし、これは私の問題です:
public void onData(Data data) {
// SNIP: Use proxy factory (not shown) to get message class and
// dynamic proxy object appropriate to the 3rd party data.
Class<?> messageClass; // e.g. = MyMessage.class;
Object dynamicProxy; // e.g. = DynamicProxy for MyMessage.class;
// TODO: How to I pick the appropriate MessageListener and dispatch the
// dynamicProxy in a type safe way? See below.
}
タイプを使用して使用した場合、データはデータを発送できません。
// Assuming a listener has been registered for the example:
MessageListener<?> listener = messageClassToListenerMap.get(messageClass);
listener.onMessage(dynamicProxy); // ERROR: can't accept Object.
listener.onMessage(messageClass.cast(dynamicProxy); // ERROR: Wrong capture.
リスナーがどのような種類のデータを受け入れ、どのタイプのデータを渡しているのかを知ることができないので、それは理にかなっています。
しかし、私がRAWタイプを使用する場合、それは正常に機能します:
// Assuming a listener has been registered for the example:
MessageListener listener = messageClassToListenerMap.get(messageClass);
listener.onMessage(dynamicProxy); // OK, provided I always pass the correct type of object.
解決
生の種類を使用する必要はありません。ワイルドカードタイプを必要なことを行うタイプにキャストします。これは、タイプの安全性に直面して飛びます。そして、それはあなたが無視できるチェックされていないキャスト警告を与えるでしょう。しかし、それは生のタイプを使用しないことが可能であることを証明しています。
MessageListener<Object> listener = (MessageListener<Object>)messageClassToListenerMap.get(messageClass);
listener.onMessage(dynamicProxy);
他のヒント
ここではジェネリックを使用することはできません。なぜなら、正確なタイプは実行時にのみ知られているため、安全でないキャストを使用する必要があるからです。生のタイプはタイプをチェックしないため、機能します。ジェネリックはコンパイル時間でのみ機能し、ディスパッチャーは実行時間で動作します。
したがって、明示的に確認する必要があります。
MessageListener listener = messageClassToListenerMap.get(messageClass);
if(!messageClass.isAssignableFrom(dynamicProxy.getClass()))
throw new Something();
listener.onMessage(dynamicProxy);
それは間違ったデザインだと思います。私はそのようなことをすることをお勧めします:
interface MyMessageListener
{
void onMessageA(String name);
void onMessageB(String otherParam);
}
インターフェイスクラスとメソッド名でメッセージをディスパッチできる場合。 (単一の方法でインターフェイスを使用できますが、それほど素敵ではありません)。さらに、スプリングにはすでにインフラストラクチャがあります。 MethodInterceptor
, RemoteExporter
, RemoteInvocation
そしていくつかの関連。
私はあなたがコードを見るとき、私はトラブルを持っています、なぜあなたはあなたのメッセージクラスにプロキシを使用するのですか...メッセージはメッセージイベントに署名するためのJava Beanです。メッセージが発生していました、あなたはリスナーの役割をするために何かをするべきです...これはメッセージの役割です....私はメッセージのためにプロキシがある理由をKONWしません、それはとても驚きです......