views:

59

answers:

2

I have a class AllListener to encapsulate multiple Listeners as follows. The problem is I have to write a loop in each event method(onStart(), onEnd()). It's quite normal way in observer pattern code, but it's bad smell. Any better way to write loop once? Thank you!

class AllListener{

    List<Listener> listeners;

    void onStart(){
        for(Listener l:listeners)//loop
            l.onStart();
    }

    void onEnd(){
        for(Listener l:listeners)//loop
            l.onEnd();
    }
}
+1  A: 

Avoiding this is hard, as Java still has no closures. Basically you have these choices:

  • to use a wrapper class for your "actions"
  • to to use reflection (which I would consider way too complicated here)
  • to use a library (e.g. functionaljava)
  • to generate code using Java's annotation processor [credits to: Little Bobby Tables]

.

class AllListener{
    List<Listener> listeners;

    private interface Wrapper {
      public void run(Listener l);
    }

    void onStart(){
        loop(new Wrapper(){
           public void run(Listener l) {
              l.onStart();
           }); 
    }

    void onEnd(){
        loop(new Wrapper(){
           public void run(Listener l) {
              l.onEnd();
           }); 
    }

    private void loop(Wrapper w) {
       for(Listener l:listeners) 
            w.run(l);
    } 
 }

As you can see, that works, but is less readable as the original version and isn't worth the trouble if you have only two calling methods.

Landei
There's a forth option: Generate code using Java's annotation processor. However, unless you have dozens of observed functions, this will be really bad practice.
Little Bobby Tables
Correct, I forgot that. I'd say the complexity of that is similar to a reflection approach.
Landei
A: 

You can write a fire method which wraps the loop and call this method from your onStart, onEnd methods as the following.

class AllListener {
    void onStart(){
        fire("onStart");
    }

    void onEnd(){
        fire("onEnd");
    }

    // eventName must be same with the event handler method in Listener class.
    private void fire(String eventName) {
        for(Listener l : listeners) {
            // Call event handler method with reflection.
            l.getClass().getMethod(eventName).invoke(l);
        }
    }
}
ovunccetin