views:

332

answers:

3

I'm trying to implement the Event Generator Idiom (http://www.javaworld.com/javaworld/jw-09-1998/jw-09-techniques.html). I find things to be a bit "odd" when it comes to the observable class though. Let's say I have the following classes:

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.

In the link I gave initially he recommends to "Name the event propagator method fire[listener-method-name].". I find this to be redundant: making a snapshot and iterating over the listeners in each fire-method, when the only thing changing is which method to call on the interface. Thus I have made a single fireEvent method, and it is working sort of. The problem is to keep the datatype of the event parameter in fireEvent "in sync" with the methods defined in BakeryListeners. Currently the fireEvent looks like this (excerpt):

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

... etc. I guess I could use an enum instead of a string to make it impossible to call fireEvent with an eventType that doesn't exist, but I still have to map Type.RECEIVED to listener.orderReceived etc?

Is it possible for the fireEvent method to take the BakeryListeners methods as a parameter? I.e (pseudo-code) method declaration:

fireEvent(BakeryListeners.methods eventType, Order source)

and then just call the appropriate method directly inside fireEvent(without if/switching):

call(listener, eventType( source))

Then it also would be impossible to create a event who isn't defined in the interface BakeryDAO.takeOrder() -> fireEvent(eventWhoDoesntExist) -> exception?

Is it possible to do this in Java? Or a better way if I've understood things wrong?

A: 

In my implementations of these kinds of systems I have the event (BakeryEvent above) carry some information about what has happened and then let the listener decide what to do (if anything) when they receive the event. That leads to a simple inteface like

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

In java there are no generic methods that will support you in a scheme where you want a "default" method to be invoked unfortunately.

Len

LenW
A: 

Creating one method per “event” results in much cleaner source code and is thus easier to read. Do not put too much information into the event object, instead rely on method definitions in your event listener interface. There’s really no point in trying to “optimize” your source code by cutting down on the number of defined methods.

Also, this way you will not run into the problem that an unknown event might be called.

(Something completely different: don’t clone the list of listeners each time you fire an event but use a CopyOnWriteArrayList, it will take care of that for you.)

Bombe
+1  A: 

I think Enumerating the event types and including the event type in the Event is cleaner that one method per event type.

First you avoid all baker listeners from having to implement all of the methods and then leave them blank if they are only interested in one event. (Have you ever implemented MouseListener?)

Second you can add to you Enum later when you figure out you need orderBilled and orderPaied with out adding more methods to all of your listeners that don't need to process that type of event.

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

Then

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!

}
Clint