java 简单的java消息调度系统

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/937302/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 14:26:38  来源:igfitidea点击:

Simple java message dispatching system

javadesign-patternsevents

提问by Brandon Yarbrough

I'm working on a little Java game in which all sorts of events can happen. There are at least a couple of dozen basic events that various event handlers may be interested in. There are also several places in the code where these events might be triggered. Rather than forcing the event listeners to know which class they need to register with, I'd like to create some sort of a centralized message-dispatching system, which some classes would submit events into and interested classes could hook into to listen for certain kinds of events.

我正在开发一个小型 Java 游戏,其中可能发生各种事件。至少有几十个基本事件是各种事件处理程序可能感兴趣的。在代码中还有几个地方可能会触发这些事件。我不想强迫事件侦听器知道他们需要注册哪个类,而是想创建某种集中式消息分发系统,某些类会将事件提交到该系统中,感兴趣的类可以挂钩以侦听某些类型的事件。

But I have some questions. Firstly, this seems like an obvious and common problem. Are there favorite implementations of simple, in-VM messaging systems? Seems like there would be.

但我有一些问题。首先,这似乎是一个明显而普遍的问题。是否有最喜欢的简单的虚拟机内消息传递系统的实现?好像会有。

Secondly, and more importantly, I'm trying to work out a reasonably elegant way for the dispatching class to know as little as possible about types of messages. I'd love to be able to create new kinds of events without modifying the message dispatcher at all. However, I have an opposite concern. I'd really like for the method signatures of the handling methods to be clear. In other words, I would prefer the following:

其次,更重要的是,我正在尝试为调度类找出一种相当优雅的方式,以便尽可能少地了解消息类型。我希望能够在不修改消息调度程序的情况下创建新类型的事件。但是,我有相反的担忧。我真的很希望处理方法的方法签名很清楚。换句话说,我更喜欢以下内容:

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

over something more generic and harder to read:

关于更通用且更难阅读的内容:

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

But I don't see any good ways to keep type-specific knowledge out of the dispatcher while at the same time keeping the method signatures clean and readable.

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

Ideas?

想法?

回答by Laurent Simon

If there is a specific listener interface for each event. Each event is able to issue listeners calls itself. Then, the role of the dispatcher is to identify target listeners and to trigger the event notification on them.

如果每个事件都有特定的侦听器接口。每个事件都能够发出侦听器调用本身。然后,调度器的作用是识别目标侦听器并触发对它们的事件通知。

For example, a generic event definition can be:

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

public interface GameEvent<L> {

   public void notify( final L listener);
}

If your CollisionListener is:

如果您的 CollisionListener 是:

public interface CollisionListener {

    public void spaceshipCollidedWithMeteor( Spaceship spaceship, Meteor meteor );

}

Then, the corresponding event can be:

那么,对应的事件可以是:

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

}

You can imagine a dispatcher which is able to propagate this event on the target listeners like in the following scenario (Events is the dispatcher class):

您可以想象一个调度程序能够在目标侦听器上传播此事件,如下面的场景(事件是调度程序类):

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

In this scenario, the dispatcher did not require any knowledge on events and listeners. It triggers an individual event notification to each listener, using only the GameEvent interface. Each event/listener pair chooses it's own dialog modalities (they can exchange many messages if they want).

在这种情况下,调度程序不需要任何关于事件和侦听器的知识。它仅使用 GameEvent 接口向每个侦听器触发单独的事件通知。每个事件/侦听器对选择自己的对话方式(如果需要,它们可以交换许多消息)。

A typical implementation of a such dispatcher should be something like:

这种调度程序的典型实现应该是这样的:

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

}   

I suppose its fulfills your requirements:

我想它满足您的要求:

  • very light,
  • fast,
  • no casts (at usage),
  • Every thing is checked at compile time (no possible mistake),
  • No API constraints on listeners (each event choose it's own messages),
  • Evolutive (no dependencies between different events and/or listeners),
  • The dispatcher is a generic black box,
  • Consumers and producers don't need to know each other.
  • 很轻,
  • 快速地,
  • 没有强制转换(使用时),
  • 在编译时检查每件事(没有可能的错误),
  • 对侦听器没有 API 限制(每个事件选择它自己的消息),
  • 进化(不同事件和/或侦听器之间没有依赖关系),
  • 调度员是一个通用的黑匣子,
  • 消费者和生产者不需要相互了解。

回答by Eddie

If you want to avoid instanceof, then your only bet is to use inheritance to route a method call to the right method. You cannot use method overloading, since that is decided at compile time by the declaredtype of the variable you are passing to a method. You have to use inheritance.

如果您想避免instanceof,那么您唯一的选择是使用继承将方法调用路由到正确的方法。您不能使用方法重载,因为这是在编译时由传递给方法的变量的声明类型决定的。你必须使用继承。

If you cannot use inheritance, then your only other choice (that I'm aware of) involves a lot of instanceof.

如果您不能使用继承,那么您唯一的其他选择(我知道)涉及很多instanceof.

Regarding a messaging system, you can use ActiveMQas an in-the-JVM message transport. You do not have to use it via socket or other means. I can't imagine that ActiveMQ would not be efficient enough for your means.

关于消息传递系统,您可以使用ActiveMQ作为 JVM 中的消息传输。您不必通过套接字或其他方式使用它。我无法想象 ActiveMQ 的效率不足以满足您的需求。

回答by Tim Williscroft

Java beans should have had this interface: it makes life simpler.

Java bean 应该有这个接口:它让生活更简单。

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

Implement it all over the place.

到处执行它。

Make a blackboard class (probably a singleton. this is a sketch only)

制作一个黑板课(可能是一个单身人士。这只是一个草图)

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


}

Give Blackboard a thread, listen for events and republish using its own thread.

给 Blackboard 一个线程,监听事件并使用它自己的线程重新发布。

Classes can just publish their events to the blackboard.

班级可以将他们的事件发布到黑板上。

Subscribe to the blackboard for events.

订阅黑板事件。

If you want you can persist events, allow republishing etc.

如果您愿意,您可以保留事件,允许重新发布等。

For something inside an app it is very good. (works just as well as a data interchange interface as well!)

对于应用程序中的某些内容,它非常好。(和数据交换接口一样好用!)