代码之家  ›  专栏  ›  技术社区  ›  Mads Mobæk

Java中事件生成器的实现

  •  0
  • Mads Mobæk  · 技术社区  · 15 年前

    我正在尝试实现事件生成器习惯用法( 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方法作为参数?即(伪代码)方法声明:

    fireEvent(BakeryListeners.methods eventType, Order source)
    

    然后直接在fireevent内部调用适当的方法(不使用if/switching):

    call(listener, eventType( source)) 
    

    那么也就不可能创建在接口bakerydao.takeOrder()->fireEvent(eventWhoDoesNTexist)->exception中未定义的事件?

    在Java中可以这样做吗?或者如果我理解错了更好的方法?

    3 回复  |  直到 15 年前
        1
  •  1
  •   Clint    15 年前

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

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

    第二,您可以添加到EnUM之后,当您确定需要OrthBar和OrdPaPIDE,并向不需要处理该类型事件的所有听众添加更多方法时。

    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!
    
    }
    
        2
  •  0
  •   LenW    15 年前

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

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

    在Java中,没有一种泛型方法可以支持您在一种方案中需要调用一个“默认”方法。

    伦恩

        3
  •  0
  •   Bombe    15 年前

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

    另外,这样就不会遇到调用未知事件的问题。

    (完全不同的是:不要在每次触发事件时克隆侦听器列表,而是使用 CopyOnWriteArrayList ,它会帮你处理的。)