Pregunta

Objetivo

Estoy tratando de construir un MessageDisPatcher que convierta los mensajes de una API de terceros en mensajes definidos por el usuario y luego los envíe a un oyente registrado por el usuario.

Se espera que el usuario:

  1. Defina una interfaz para cada tipo de mensaje de usuario.
  2. Registre un oyente con el despachador de mensajes para cada tipo de mensaje.
  3. Pase los datos sin procesar/terceros al despachador de mensajes.
  4. Manejar los mensajes transmitidos a los oyentes.

Descripción del problema

Desafortunadamente, parece que no puedo evitar usar un tipo sin procesar para lograr mi API deseada. Leí en otros lugares que no hay casos excepcionales para usar tipos sin procesar y que solo existen en el idioma para la compatibilidad con versiones anteriores.

¿Hay alguna manera de cambiar el código a continuación para que funcione o necesito rediseñar mi API?

Interfaces

El MessageDisPatcher implementa la siguiente interfaz:

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

}

La interfaz MessageListener se define como:

public interface MessageListener<T> {

    public void onMessage(T message);   

}

Un ejemplo de mensajes de usuario puede verse así:

public interface MyMessage {

    public String getName();   

}

Registrando oyentes

El usuario puede registrar un oyente de la siguiente manera:

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

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

Un despachador de mensajes estándar puede implementar el método como este:

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.

}

Envío de mensajes

Cuando el MessageDisPatcher recibe un nuevo mensaje, crea un proxy dinámico para el objeto y lo envía a un oyente apropiado. Pero aquí es donde está mi problema:

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.

}

Si intento usar el tipo que no puedo enviar los datos:

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

Tiene sentido, porque no hay forma de que pueda saber qué tipo de datos acepta mi oyente y qué tipo de datos lo estoy pasando.

Pero si uso tipos en bruto funciona bien:

// 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.
¿Fue útil?

Solución

No necesita usar tipos RAW, simplemente arroje los tipos comodín en un tipo que haga lo que desea. Esto vuela frente a la seguridad del tipo. Y dará una advertencia de elenco sin control, que puedes ignorar. Pero demuestra que es posible no usar tipos sin procesar.

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

listener.onMessage(dynamicProxy);

Otros consejos

No puede usar genéricos aquí, porque el tipo exacto se conoce solo en el tiempo de ejecución, por lo que debe usar el reparto inseguro. Los tipos en bruto no verifican los tipos, por lo tanto, funciona. Los genéricos funcionan solo en el tiempo de compilación, su despachador funciona en tiempo de ejecución.

Por lo tanto, debe verificarlo explícitamente:

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

Creo que es un diseño incorrecto. Recomendaría hacer algo así:

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

Cuando puede enviar mensajes por la clase de interfaz y el nombre del método. (Podrías usar interfaces con un solo método, pero no tan bien en mi humilde opinión). Además, la primavera ya tiene infraestructura para ello: MethodInterceptor, RemoteExporter, RemoteInvocation y algunos relacionados.

Tengo un trubble cuando te veo código, ¿por qué usas la clase de mensajes proxy para tu ... el mensaje es solo un java bean para firmar un evento de mensaje? Mensaje había sucedido, debes hacer algo para que hagas el papel de un oyente ... este es el papel de un mensaje ... No sé por qué hay un proxy para el mensaje, es tan sorpresa ...

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top