Как избежать необработанных типов в диспетчере сообщений Java - PullRequest
4 голосов
/ 10 января 2012

Цель

Я пытаюсь создать MessageDispatcher, который преобразует сообщения из стороннего API в определенные пользователем сообщения и затем отправляет их зарегистрированному слушателю.

Ожидается, что пользователь:

  1. определит интерфейс для каждого типа пользовательского сообщения.
  2. Зарегистрируйте слушателя в диспетчере сообщений для каждого типа сообщения.
  3. Передача необработанных / сторонних данных диспетчеру сообщений.
  4. Обработка сообщений, передаваемых обратно слушателям.

Описание проблемы

К сожалению, яне могу избежать использования необработанного типа для достижения желаемого API.Я читал в другом месте, что нет исключительных случаев использования типов Raw, и они существуют только в языке для обратной совместимости.

Есть ли способ, которым я могу изменить приведенный ниже код для работы, или мне нужно изменить дизайнМой API?

Интерфейсы

MessageDispatcher реализует следующий интерфейс:

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.

}

Отправка сообщений

Когда MessageDispatcher получает новое сообщение, он создает динамический прокси для объекта и отправляет его соответствующему слушателю.Но вот где моя проблема:

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.

Это имеет смысл, потому что я никак не могуможет знать, какой тип данных принимает мой слушатель, и какой тип данных я передаю.

Но если я использую необработанные типы, он отлично работает:

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

Ответы [ 3 ]

1 голос
/ 11 января 2012

Вам не нужно использовать необработанные типы - просто приведите подстановочные типы к типу, который делает то, что вы хотите.Это своего рода бросает вызов безопасности типа.И это даст непроверенное предупреждение, которое вы можете проигнорировать.Но это доказывает, что можно не использовать необработанные типы.

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

listener.onMessage(dynamicProxy);
1 голос
/ 10 января 2012

Вы не можете использовать дженерики здесь, потому что точный тип известен только во время выполнения, поэтому вы должны использовать небезопасное приведение.Необработанные типы не проверяют типы, следовательно, это работает.Обобщения работают только во время компиляции, ваш диспетчер работает во время выполнения.

Итак, вы должны явно проверить это:

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

, когда вы можете отправлять сообщения по классу интерфейса и имени метода.(вы можете использовать интерфейсы с одним методом, но не очень приятно, imho).Более того, у Spring уже есть инфраструктура для этого: MethodInterceptor, RemoteExporter, RemoteInvocation и некоторые другие.

0 голосов
/ 10 января 2012

Когда я вижу ваш код, у меня возникает проблема, почему вы используете прокси для своего класса сообщений ... сообщение - это просто Java-бин, чтобы подписать событие сообщения, вы просто сообщаете слушателю, что естьпроизошло сообщение какого-то типа, вы должны сделать что-то, чтобы иметь роль слушателя ... это роль сообщения .... я не знаю, почему существует прокси для сообщения, это так удивительно ......

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...