简单的java消息调度系统

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

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

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

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 { public void notify( final L listener); } 

如果你的CollisionListener是:

 public interface CollisionListener { public void spaceshipCollidedWithMeteor( Spaceship spaceship, Meteor meteor ); } 

然后,相应的事件可以是:

 public final class Collision implements GameEvent { 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 map = new HashMap( 10 ); /** Add a listener to an event class **/ public  void listen( Class> evtClass, L listener) { final ArrayList listeners = listenersOf( evtClass ); synchronized( listeners ) { if ( !listeners.contains( listener ) ) { listeners.add( listener ); } } } /** Stop sending an event class to a given listener **/ public  void mute( Class> evtClass, L listener) { final ArrayList listeners = listenersOf( evtClass ); synchronized( listeners ) { listeners.remove( listener ); } } /** Gets listeners for a given event class **/ private  ArrayList listenersOf(Class> evtClass) { synchronized ( map ) { @SuppressWarnings("unchecked") final ArrayList existing = map.get( evtClass ); if (existing != null) { return existing; } final ArrayList emptyList = new ArrayList(5); map.put(evtClass, emptyList); return emptyList; } } /** Notify a new event to registered listeners of this event class **/ public  void notify( final GameEvent evt) { @SuppressWarnings("unchecked") Class> evtClass = (Class>) evt.getClass(); for ( L listener : listenersOf( evtClass ) ) { evt.notify(listener); } } } 

我想它符合你的要求:

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

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

如果你不能使用inheritance,那么你唯一的另一个选择(我知道)涉及很多instanceof

关于消息传递系统,您可以将ActiveMQ用作JVM内部消息传输。 您不必通过套接字或其他方式使用它。 我无法想象ActiveMQ对你的手段不够高效。

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

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

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

类可以只将事件发布到黑板上。

订阅黑板上的活动。

如果你想要你可以坚持事件,允许重新发布等。

对于应用程序内部的东西,这是非常好的。 (和数据交换接口一样工作!)