Тонны событий, способы уменьшить кодовую базу? - PullRequest
1 голос
/ 04 января 2011

В моей программе есть около 50 событий, которые выбрасываются. У меня возникла проблема со стандартным способом получения событий.

Моей первой мыслью было использовать универсальный интерфейс и просто заставить слушателей реализовать это и добавить себя в очередь слушателей. К сожалению, это означало, что классы не могли иметь несколько слушателей из-за стирания типов и проблем множественного наследования . Поэтому я удалил код слушателя и начал заново.

Моя текущая (и оригинальная) установка состоит в том, чтобы иметь интерфейс, который прослушивает каждое событие. Например,

/**
 * Listener for {@link myproject.hooks.events.FingerEvent}events
 * @see myproject.hooks.events.Finger
 */
 public interface FingerListener extends Listener {
        /**
         * Invoked when an {@link myproject.hooks.events.FingerEvent}occurs
         * @param event The generated FingerEvent
         */
        public void onFinger(FingerEvent event);
}

Все они расширяют интерфейс Listener, который не содержит никаких методов, он просто так, что их все вместе можно назвать "Слушателями".

Проблема в том, что на событие приходится тонна кода. У вас есть событие и его получатели (к счастью, это было упрощено Project Lombok), затем интерфейс, который его получает, затем Javadoc на интерфейсе, чтобы, когда кто-то хочет получить информацию, он знает, где ее получить.

Другая проблема, с которой я сталкиваюсь, заключается в том, что у каждого слушателя есть свое имя метода, что приводит к очень интересному коду, пытающемуся выяснить, какой метод вызывать. Он хрупкий, медленный (использует отражение) и выглядит как дерьмо. Если вы мне не верите, тогда:

    public static boolean callListener(Event event, Listener listener) {            
            //Get base name of event
            String name = event.getClass().getSimpleName().split("Event")[0];

            //Try and get the correct method if it exists
            Method listenerMethod = null;
            try {
                    listenerMethod = listener.getClass().getMethod("on"+name, event.getClass());
            } catch (NoSuchMethodException ex) {
                    //Method doesn't exist, just don't call anything
                    return false;
            } catch (SecurityException ex) {
                    throw new RuntimeException("Method on"+name+" is unaccessable", ex);
            }

            //Now that we have the method, attempt to execute it
            try {
                    listenerMethod.invoke(listener, event);
            } catch (Exception ex) {
                    throw new RuntimeException("Unexpected error when invoking method on"+name);
            }

            //Method executed sucessfully, return true
            return true;
    }

Это хотя и стандартный способ получения событий в java: есть интерфейс слушателя для каждого события, затем создать спагетти для вызова соответствующих методов, или я делаю это совершенно неправильно?

Ответы [ 2 ]

2 голосов
/ 04 января 2011

По крайней мере, вы можете избавиться от логики отражения / вызова, упростив делегирование «обработки событий» слушателю.Слушатель знает, какой тип событий он может обрабатывать, поэтому просто добавьте один метод к интерфейсу маркера, например:

 public interface Listener {
   public boolean process(Event event);
 }

и измените код следующим образом:

 public static boolean callListener(Event event, Listener listener) {   
    return listener.process(event);
 }

Теперь,если у нас есть Listener, который понимает события Breakfast и Dinner, мы можем реализовать его следующим образом (в MealListener):

public class MealListener implements Listener {

  @Override
  public boolean process(Event event) {
    if (event instanceof Breakfast) {
       this.onBreakfast((Breakfast) event);
       return true;
    }
    if (event instanceof Dinner) {
       this.onDinner((Dinner) event);
       return true;
    }
    return false; // MealListener ignores this event
  }

  private void onBreakfast(Breakfast breakfastCall) {
    // eat breakfast
  }

  private void onDinner(Dinner dinnerCall) {
    // eat dinner
  }

} 

Кстати, не надобоятся "тонны классов" - просто найдите общий исходный шаблон для всех событий и слушателей и автоматически сгенерируйте исходные файлы.В этом случае вам не нужно поддерживать отдельные исходные файлы событий и слушателей, а только генератор кода и его файл ресурсов (список на основе файлов с базовыми именами для всех событий и слушателей)

0 голосов
/ 04 января 2011

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

Итак, ваш интерфейс должен выглядеть примерно так:

public interface FingerListener {
   public void listenerCallback(FingerEvent event);
}

И тогда вы можете просто перебрать своих слушателей и вызвать метод listenerCallback для каждого зарегистрированного слушателя.

Если получателю необходимо различать различные типы событий, я бы добавил свойство "код типа" в класс FingerEvent, который может проверяться получателем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...