在Java中实现事件生成器习惯

时间:2009-04-30 10:16:56

标签: java design-patterns event-handling

我正在尝试实现事件生成器成语(http://www.javaworld.com/javaworld/jw-09-1998/jw-09-techniques.html)。我发现,当涉及到可观察的类时,事情有点“奇怪”。假设我有以下课程:

interface BakeryListener  
+ orderReceived(BakeryEvent event)  
+ orderProcessing(BakeryEvent event)  
+ orderFinished(BakeryEvent event)  
+ orderDelivered(BakeryEvent event)  

LogView, OrderReadyView etc. implements BakeryListener  
Creates a GUI for each own use

Order  
VO / DTO object, used as the source in BakeryEvent 

BakeryDAO (the observable)  
- orders : Vector  
- listeners : Vector  
+ takeOrder, cancelOrder, bake and other regular DAO methods, 
triggering an event by calling fireEvent
+ addBakeryListener(BakeryEvent event)  
+ removeBakeryListener(BakeryEvent event)  
- fireEvent(Order source, EVENTTYPE????)

BakeryGUI  
Creates/gets a reference to BakeryDAO. Creates and attaches LogView, OrderReadyView as listeners on BakeryDAO.

在我最初给出的链接中,他建议“命名事件传播方法fire [listener-method-name]。”。我发现这是多余的:制作快照并在每个fire方法中迭代监听器,当唯一改变的是在接口上调用哪个方法时。因此,我已经制作了一个fireEvent方法,它正在起作用。问题是使fireEvent中的事件参数的数据类型与BakeryListeners中定义的方法保持“同步”。目前fireEvent看起来像这样(摘录):

for(BakeryListener listener : copyOfListeners){
    if(eventType.equals("received")) listener.orderReceived(event);
    else if(eventType.equals("processing")) listener.orderProcessing(event);
}

......等我想我可以使用枚举而不是字符串来使用不存在的eventType调用fireEvent是不可能的,但我还是要将Type.RECEIVED映射到listener.orderReceived等?

fireEvent方法是否可以将BakeryListeners方法作为参数? I.e(伪代码)方法声明:

fireEvent(BakeryListeners.methods eventType, Order source)

然后直接在fireEvent中调用适当的方法(不用if / switching):

call(listener, eventType( source)) 

然后也不可能创建一个未在InterfaceDAO.takeOrder()接口中定义的事件 - > fireEvent(eventWhoDoesntExist) - >异常?

是否可以在Java中执行此操作?或者如果我理解错误会有更好的方法吗?

3 个答案:

答案 0 :(得分:1)

我认为枚举事件类型并在事件中包含事件类型比每个事件类型更清楚一个方法。

首先,您要避免所有baker侦听器必须实现所有方法,如果他们只对一个事件感兴趣,则将它们留空。 (你有没有实现过MouseListener?)

其次,当你发现需要orderBilled和orderPaied时,你可以向你添加Enum,而不需要为所有不需要处理该类型事件的侦听器添加更多方法。

public class BakeryEvent {
  public enum Type { Received, Processing,Finished,Delivered,Billed,Paied };

  private Type myType;
  private Order myOrder;

  BakeryEvent( Order order, BakeryEvent.Type bet ) {//...

  }
  //...
}


public interface BakeryListener {
  public void handleBakeryEvent( BakeryEvent be );
}

然后

public class OvenScheduler implements BakeryListener {

  public void handleBakeryEvent( BakeryEvent be ){
    if ( BakeryEvent.Type.Received.equals(be.getEventType() ) {
      scheduldeOven( be.getOrder() );
    }
  }
  // hey look I don't have to implement orderBilled() and then do nothing!

}

答案 1 :(得分:0)

在我对这些系统的实现中,我有事件(上面的BakeryEvent)带有关于发生了什么的一些信息,然后让听众决定在接收事件时做什么(如果有的话)。这导致了一个简单的界面,如

for (Listener l : registeredListeners) {
  l.processEvent(event);
}

在java中,没有通用的方法可以支持你想要在其中调用“默认”方法的方案。

莱恩

答案 2 :(得分:0)

每个“事件”创建一个方法会产生更清晰的源代码,因此更容易阅读。不要在事件对象中放入太多信息,而是依赖事件监听器接口中的方法定义。尝试通过减少已定义方法的数量来“优化”源代码是没有意义的。

此外,这样您就不会遇到可能会调用未知事件的问题。

(完全不同的东西:每次发起事件时都不要克隆侦听器列表但使用CopyOnWriteArrayList,它会照顾你。)