小编典典

简单的Java消息调度系统

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();
        //...
     }
   }
}

但是,我看不出有什么好方法可以将特定于类型的知识排除在调度程序之外,同时又可以使方法签名清晰易读。

有想法吗?


阅读 361

收藏
2020-12-03

共1个答案

小编典典

每个事件是否有特定的侦听器接口。每个事件都可以发出侦听器调用。然后,调度程序的作用是识别目标侦听器并在其上触发事件通知。

例如,通用事件定义可以是:

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

}

您可以想象一个调度程序,它能够像以下情况一样在目标侦听器上传播此事件(Events是调度程序类):

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

}

我想它可以满足您的要求:

  • 很轻,
  • 快速,
  • 没有强制转换(使用时),
  • 在编译时检查所有内容(没有可能的错误),
  • 侦听器没有API约束(每个事件选择它自己的消息),
  • 进化(不同事件和/或侦听器之间没有依赖关系),
  • 调度员是一个通用的黑匣子,
  • 消费者和生产者不需要彼此了解。
2020-12-03