Избегайте многократного цикла в коде Observer - PullRequest
3 голосов
/ 27 октября 2010

У меня есть класс AllListener для инкапсуляции нескольких слушателей следующим образом. Проблема в том, что я должен написать цикл в каждом методе события (onStart(), onEnd()). Это довольно обычный способ в коде шаблона наблюдателя, но это неприятный запах. Есть ли лучший способ написать цикл один раз? Спасибо!

class AllListener{

    List<Listener> listeners;

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

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

Ответы [ 3 ]

1 голос
/ 27 октября 2010

Избежать этого сложно, поскольку в Java до сих пор нет замыканий. В основном у вас есть следующие варианты:

  • чтобы использовать класс-оболочку для ваших "действий"
  • чтобы использовать отражение (что я бы посчитал слишком сложным)
  • для использования библиотеки (например, functionsjava )
  • для генерации кода с использованием процессора аннотаций Java [кредитов: 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);
    } 
 }

Как видите, это работает, но менее читабельно, чем оригинальная версия, и не стоит проблем, если у вас есть только два вызывающих метода.

0 голосов
/ 29 июля 2014

Вы можете использовать динамические прокси 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, который реализован таким образом, что он будет проходить через контейнер слушателей и выполнять исходный вызов для каждого элемента .

Метод можно безопасно вызывать, поскольку исходный вызов был выполнен на том же интерфейсе, который мы собираемся вызвать.

Это решение может работать, если у ваших слушателей нет возвращаемого значения.

0 голосов
/ 27 октября 2010

Вы можете написать метод fire, который оборачивает цикл, и вызвать этот метод из ваших методов 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);
        }
    }
}
...