当前位置: 首页 > 面试题库 >

简单的Java消息调度系统

太叔弘壮
2023-03-14
问题内容

我正在研究一个小型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();
        //...
     }
   }
}

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

有想法吗?


问题答案:

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

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

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约束(每个事件选择它自己的消息),
  • 进化(不同事件和/或侦听器之间没有依赖关系),
  • 调度员是一个通用的黑匣子,
  • 消费者和生产者不需要彼此了解。


 类似资料:
  • YodaOS 中通过 YodaOS Message 完成对系统中的某个模块的定制化,本章节即是列出 YodaOS 中标准的消息接口,方便有需要的开发者对模块进行定制。 YodaOS Message 使用 yodaos-project/flora 作为 IPC 方式,它支持的数据结构包括: int float long double string binary 本章节描述中出现的number格式为i

  • Running Events With a Delay # sched_basic.py import sched import time scheduler = sched.scheduler(time.time, time.sleep) def print_event(name, start): now = time.time() elapsed = int(now -

  • 本文向大家介绍Python使用Redis实现作业调度系统(超简单),包括了Python使用Redis实现作业调度系统(超简单)的使用技巧和注意事项,需要的朋友参考一下 概述 Redis是一个开源,先进的key-value存储,并用于构建高性能,可扩展的Web应用程序的完美解决方案。 Redis从它的许多竞争继承来的三个主要特点: Redis数据库完全在内存中,使用磁盘仅用于持久性。 相比许多键值数

  • 我是Kafka的新手,运行一个简单的Kafka消费者/生产者的例子,就像在Kafka消费者和KafkaProducer上给出的那样。当我从终端运行消费者时,消费者正在接收消息,但我不能使用Java代码监听。我也在StackoverFlow上搜索了类似的问题(链接: Link1,Link2),并尝试了解决方案,但似乎没有什么对我有用。kafka版本:和相应的maven依赖在pom中使用。 Java生

  • 我有一个简单的camel MINA服务器,使用JAVA DSL,我的运行方式与这里记录的示例类似: 独立运行骆驼并让它在JAVA中继续运行 MINA 2组件 我正在尝试创建一个托管在mina:tcp://localhost:9991(又名MyApp_B)的示例应用程序,该应用程序向托管在mina:tcp://localhost:9990(又名MyApp_A)的服务器发送一个非常简单的消息。 我想发

  • Cocos Creator 3D 内有许多独立运行的的进程,这些进程间是相互隔离的。 所以在编辑器内需要与其他功能进行交互的时候,需要通过 "消息" 进行交互。 编辑器里的 "消息系统" 是 IPC(进程间通信)的功能扩展封装。这个系统承担起了整个编辑器内通讯交互的重担。 消息类型 消息交互分成了两种情况: 主动发送某条消息到某个功能(扩展) 某个功能(扩展)完成了一个操作后向所有人发送通知,告知