Avoid multiple loops in Observer code

I have a class AllListenerfor encapsulating multiple Listeners as follows. The problem is that I have to write a loopin each event method ( onStart(), onEnd()). This is a perfectly normal path in the observer pattern code, but it smells bad. Any better way to write a loop once? Thank!

class AllListener{

    List<Listener> listeners;

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

    void onEnd(){
        for(Listener l:listeners)//loop
            l.onEnd();
    }
}
+3
source share
3 answers

Avoiding this is difficult because Java still has no closures. Basically you have the following options:

  • use a wrapper class for your "actions"
  • use reflection (which I find too complicated here)
  • (, functionaljava)
  • Java [: Bobby]

.

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

, , , .

+1

, onStart, onEnd .

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

java :

public class MultiListenerExample {

    private ArrayList<OnClickListener> onClickListeners = new ArrayList<OnClickListener>();     private OnClickListener dispatcher;

    public void performOnClick(View v) {
        dispatch().onClick(v);
    }       

    private OnClickListener dispatch() {
        if (dispatcher == null) {
            dispatcher = createDispatcher();
        }

        return dispatcher;
    }

    private OnClickListener createDispatcher() {
        ClassLoader loader = OnClickListener.class.getClassLoader();
        Class<?>[] interfaces = new Class[] { OnClickListener.class };
        InvocationHandler handler = new InvocationHandler() {

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                for (OnClickListener listener : onClickListeners) {
                    // safe to call this since we implement the same interface as the object of the original invocation
                    method.invoke(listener, args);  
                }   
                return null;
            }
        };  
        return (OnClickListener) Proxy.newProxyInstance(loader, intefaces, handler);
    }   
}

, dispatch(), InvocationHandler, , .

This method can be safely called because the original call was made on the same interface that we are going to call.

This solution may work as long as your listeners have no return value.

0
source

Source: https://habr.com/ru/post/1771648/


All Articles