2009-06-01 2 views
18

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

Но у меня есть некоторые вопросы. Во-первых, это кажется очевидной и распространенной проблемой. Есть ли любимые реализации простых систем обмена сообщениями в VM? Похоже было бы.

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

public class CollisionConsoleHandler implements CollisionListener { 
    @Override 
    public void spaceshipCollidedWithMeteor(Spaceship spaceship, Meteor meteor) { 
     //... 
    } 
} 

над чем-то более общего и сложнее читать:

public class CollisionConsoleHandler implements GameMessageListener { 
    @Override 
    public void handleMessage(GameMessage message) { 
    if(message instanceof SpaceshipCollisionMessage) { 
     Spaceship spaceship = ((SpaeshipCollisionMessage)message).getSpaceship(); 
     Meteor meteor = ((SpaeshipCollisionMessage)message).getMeteor(); 
     //... 
    } 
    } 
} 

Но я не вижу каких-либо хороших способов, чтобы сохранить знания типоспецифической из диспетчера, в то же время сохраняя чистоту и удобочитаемость подписи метода.

Идеи?

+2

Поскольку этот вопрос пришел и ушел несколько лет назад, у google guava есть диспетчер событий EventBus –

ответ

40

Если для каждого события имеется конкретный интерфейс прослушивателя. Каждое событие может выдавать сами вызовы слушателей. Затем роль диспетчера заключается в том, чтобы определить целевых слушателей и инициировать уведомление о событиях на них.

Например, общее определение события может быть:

public interface GameEvent<L> { 

    public void notify(final L listener); 
} 

Если CollisionListener является:

public interface CollisionListener { 

    public void spaceshipCollidedWithMeteor(Spaceship spaceship, Meteor meteor); 

} 

Затем соответствующее событие может быть:

public final class Collision implements GameEvent<CollisionListener> { 

    private final Spaceship ship; 
    private final Meteor meteor; 

    public Collision(final Spaceship aShip, final Meteor aMeteor) { 
     this.ship = aShip; 
     this.meteor = aMeteor; 
    } 

    public void notify(final CollisionListener listener) { 
     listener.spaceshipCollidedWithMeteor(ship, meteor); 
    } 

} 

Вы можете представить диспетчера, который может распространять это событие на целевых слушателей, как в следующем Сценарий (события диспетчера класс):

// A unique dispatcher 
final static Events events = new Events(); 

// Somewhere, an observer is interested by collision events 
CollisionListener observer = ... 
events.listen(Collision.class, observer); 

// there is some moving parts   
Spaceship aShip = ... 
Meteor aMeteor = ... 

// Later they collide => a collision event is notified trough the dispatcher 
events.notify(new Collision(aShip, aMeteor )); 

В этом случае диспетчер не требует каких-либо знаний о событиях и слушателях. Он запускает индивидуальное уведомление о событии для каждого слушателя, используя только интерфейс GameEvent. Каждая пара событий/слушателя выбирает собственные методы диалога (они могут обмениваться многими сообщениями, если они захотят).

Типичная реализация такого диспетчеру должно быть что-то вроде:

public final class Events { 

    /** mapping of class events to active listeners **/ 
    private final HashMap<Class,ArrayList> map = new HashMap<Class,ArrayList >(10); 

    /** Add a listener to an event class **/ 
    public <L> void listen(Class<? extends GameEvent<L>> evtClass, L listener) { 
     final ArrayList<L> listeners = listenersOf(evtClass); 
     synchronized(listeners) { 
     if (!listeners.contains(listener)) { 
      listeners.add(listener); 
     } 
     } 
    } 

    /** Stop sending an event class to a given listener **/ 
    public <L> void mute(Class<? extends GameEvent<L>> evtClass, L listener) { 
     final ArrayList<L> listeners = listenersOf(evtClass); 
     synchronized(listeners) { 
     listeners.remove(listener); 
     } 
    } 

    /** Gets listeners for a given event class **/ 
    private <L> ArrayList<L> listenersOf(Class<? extends GameEvent<L>> evtClass) { 
     synchronized (map) { 
     @SuppressWarnings("unchecked") 
     final ArrayList<L> existing = map.get(evtClass); 
     if (existing != null) { 
      return existing; 
     } 

     final ArrayList<L> emptyList = new ArrayList<L>(5); 
     map.put(evtClass, emptyList); 
     return emptyList; 
     } 
    } 


    /** Notify a new event to registered listeners of this event class **/ 
    public <L> void notify(final GameEvent<L> evt) { 
     @SuppressWarnings("unchecked") 
     Class<GameEvent<L>> evtClass = (Class<GameEvent<L>>) evt.getClass(); 

     for (L listener : listenersOf( evtClass)) { 
     evt.notify(listener); 
     } 
    } 

} 

Я полагаю, его отрабатывает ваши требования:

  • очень легкий,
  • быстро,
  • не слепки (при использовании),
  • Все проверяется при компиляции tim е (не возможная ошибка),
  • Нет API ограничений на слушателях (каждое событие выбрать его собственные сообщения),
  • развивающих (нет зависимостей между различными событиями и/или слушателями),
  • Диспетчерским не является общей черный box,
  • Потребителям и производителям не нужно знать друг друга.
+0

Merci! Это потрясающе! Благодарю. –

+0

Решения, подобные этим, заставляют меня любить программирование: D. Merci beaucoup! – orlade

+0

Священный Удивительный. Благодаря! –

0

Если вы хотите избежать instanceof, то ваша единственная ставка - использовать наследование, чтобы направить вызов метода на правильный метод. Вы не можете использовать перегрузку метода, так как это определено во время компиляции , объявленного тип переменной, которую вы передаете методу. Вы должны использовать наследование.

Если вы не можете использовать наследование, то ваш единственный другой выбор (о котором я знаю) включает в себя много instanceof.

Что касается системы обмена сообщениями, вы можете использовать ActiveMQ в качестве транспорта сообщений в JVM. Вам не нужно использовать его через сокет или другие средства. Я не могу себе представить, что ActiveMQ не будет достаточно эффективен для ваших средств.

+0

ActiveMQ выглядит действительно круто, но это может быть просто немного меньше, чем то, что пытается сделать OP. – n3rd

0

Java-бобы должны иметь этот интерфейс: это упрощает жизнь.

interface PropertyChangeProvider { 
    void addPropertyChangeListener(PropertyChangeListener l); 
    void addPropertyChangeListener(String property, PropertyChangeListener l); 
    void removePropertyChangeListener(PropertyChangeListener l); 
    void removePropertyChangeListener(String property, PropertyChangeListener l); 
} 

Внесите его повсеместно.

Сделать доске класс (возможно одноэлементно. Это только эскиз)

public class Blackboard implements PropertyChangeListener,PropertyChangeProvider { 

static Blackboard getInstance(){ 
    // implement this 
} 

void initialise(){ 
    // start the thread here 
} 

void republish(){ 
    // this can save you heartache too. 
} 


} 

Дайте доске нить, прослушивать события и переиздать, используя свою собственную нить.

Занятия могут просто публиковать свои события на доске.

Подпишитесь на доске объявлений о событиях.

Если вы хотите, вы можете сохраняться события, позволяют переиздание и т.д.

Для чего-то внутри приложения это очень хорошо.(работает так же хорошо, как и интерфейс обмена данными!)

Смежные вопросы