Frage

Zielsetzung

Ich versuche, einen Messagedispatcher zu erstellen, der Nachrichten von einer API von Drittanbietern in benutzerdefinierte Nachrichten konvertiert, und sendet sie dann an einen benutzerregistrierten Hörer.

Der Benutzer wird erwartet:

  1. Definieren Sie eine Schnittstelle für jede Art von Benutzernachricht.
  2. Registrieren Sie einen Hörer für jeden Nachrichten -Typ mit dem Nachrichten -Dispatcher.
  3. Übergeben Sie RAW/3. Party -Daten an den Nachrichten Dispatcher.
  4. Handle Nachrichten, die an die Hörer weitergegeben wurden.

Problembeschreibung

Leider kann ich nicht vermeiden, einen Rohtyp zu verwenden, um meine gewünschte API zu erreichen. Ich habe an anderer Stelle gelesen, dass es keine außergewöhnlichen Fälle für die Verwendung von Rohtypen gibt und sie nur in der Sprache für die Rückwärtskompatibilität existieren.

Gibt es eine Möglichkeit, den folgenden Code in die Arbeit zu ändern, oder muss ich meine API neu gestalten?

Schnittstellen

Der Messagedispatcher implementiert die folgende Schnittstelle:

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);

}

Die Messagelistener -Schnittstelle ist definiert als:

public interface MessageListener<T> {

    public void onMessage(T message);   

}

Ein Beispiel -Benutzernachrichten könnte so aussehen:

public interface MyMessage {

    public String getName();   

}

Hörer registrieren

Der Benutzer kann einen Hörer wie folgt registrieren:

messageDispatcher.registerListener(MyMessage.class, 
    new MessageListener<MyMessage.class>() {
    @Override

   public void onMessage(MyMessage message) {
        System.out.println("Hello " + message.getName());
    }
}

Ein Standard -Nachrichten -Dispatcher kann die Methode wie folgt implementieren:

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.

}

Versandnachrichten

Wenn der Messagedispatcher eine neue Nachricht empfangen wird, wird ein dynamischer Proxy für das Objekt erstellt und an einen geeigneten Hörer gesendet. Aber hier ist mein Problem:

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.

}

Wenn ich versuche, den Typ zu verwenden, kann ich die Daten nicht entsenden:

// 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.

Es ist sinnvoll, weil ich auf keinen Fall wissen kann, welche Art von Daten mein Hörer akzeptiert und welche Art von Daten ich sie überlasse.

Aber wenn ich Rohtypen verwende, funktioniert es gut:

// 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.
War es hilfreich?

Lösung

Sie müssen keine Rohtypen verwenden - werfen Sie einfach die windelandierten Typen in einen Typ, der das tut, was Sie wollen. Diese irgendwie fliegt angesichts der Art von Sicherheit. Und es wird eine ungeprüfte Warnung geben, die Sie ignorieren können. Es beweist jedoch, dass es möglich ist, keine Rohtypen zu verwenden.

MessageListener<Object> listener = (MessageListener<Object>)messageClassToListenerMap.get(messageClass);

listener.onMessage(dynamicProxy);

Andere Tipps

Hier können Sie Generika nicht verwenden, da der genaue Typ nur zur Laufzeit bekannt ist, sodass Sie unsichere Besetzung verwenden müssen. Rohtypen überprüfen keine Typen, daher funktioniert es. Generika funktioniert nur in Kompilierzeit, Ihr Dispatcher arbeitet in der Laufzeit.

Sie sollten es also explizit überprüfen:

MessageListener listener = messageClassToListenerMap.get(messageClass);  
if(!messageClass.isAssignableFrom(dynamicProxy.getClass()))
  throw new Something();
listener.onMessage(dynamicProxy);

Ich denke, es ist falsches Design. Ich würde empfehlen, so etwas zu tun:

interface MyMessageListener
{
  void onMessageA(String name);
  void onMessageB(String otherParam);
}

Wenn Sie Nachrichten nach dem Schnittstellenklassen- und Methodennamen entsenden können. (Sie können Schnittstellen mit einzelnen Methoden verwenden, aber nicht so schön imho). Darüber hinaus hat die Frühling bereits eine Infrastruktur dafür: MethodInterceptor, RemoteExporter, RemoteInvocation und einige verwandte.

Ich habe ein Trubble, wenn ich Ihren Code sehe. Warum verwenden Sie den Proxy für Ihre Nachrichtenklasse ... Die Nachricht ist nur eine Java -Bean, um ein Nachrichtenereignis zu unterschreiben, Sie sagen nur dem Hörer, dass es einen Typ gibt Nachricht war geschehen, Sie sollten etwas tun, um die Rolle eines Hörers zu spielen ... Dies ist die Rolle einer Nachricht ... Ich bin nicht, warum es einen Proxy für die Nachricht gibt, es ist so überrascht ......

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top