【问题标题】:Event source: Short way to fire event事件来源:触发事件的快捷方式
【发布时间】:2014-04-05 14:33:32
【问题描述】:

我正在为我的课程编写事件源模板:

public class EventSource<E> {
  protected ArrayList<E> listeners = new ArrayList<E>();

  public void addListener(E listener)
  {
    listeners.add(listener);
  }

  public void removeListener(E listener)
  {
    listeners.remove(listener);
  }
}

要触发一个事件,我需要调用类似:

for(SomeListener listener: listeners)
  listener.onSomeEvent();

这看起来很难触发一个事件。有没有办法调用更短的东西,例如:

fire(SomeListener.onSomeEvent)

fire(onSomeEvent)

?

我已经找到了 3 个解决方案:

我使用 java 反射并将方法名称作为参数传递给模板中的某个方法,它将为所有侦听器调用此方法。但我认为它效率低下,因为需要解析字符串(方法名称)。以及如何以不丑陋的方式传递参数 - 问题仍然存在。

II 对所有方法使用相同的名称,但传递不同的参数:

public struct SomeEvent1
{
  int someData;
}

public struct SomeEvent2
{
  int someAnotherData;
  double probablyMoreData;
}

public interface SomeListener
{
  public void fire(SomeEvent1 e);
  public void fire(SomeEvent2 e);
}

但即使我不需要任何参数,它也需要构建此事件结构。

III 使用委托。想象一下,SomeEventSource 扩展了 EventSource。现在我可以调用 onSomeEventI(),因为我已经在 SomeEventSource 中编写了这些方法:

public void onSomeEventI()
{
  for(SomeListener listener: listeners)
    listener.onSomeEventI();
}

但我不能在通用 EventSource 中编写这些方法,因为它与侦听器无关。在 SomeEventSource 中编写这些方法会使泛型 EventSource 更无用,而不是有用。

问题仍然存在:如何以短的方式触发事件?

【问题讨论】:

  • 嗯……使用泛型确实不适合泛型事件处理。看一下 Java 类 Observer 和 Observable。他们不是最好的,但也许你可以接管一些部分。
  • 是的,Observer - Observerable 比编写自己的东西要好。但它与方法(II)有相同的缺点。如果有类似 std::bind 的东西(我来自 C++)绑定到函数并将其作为参数传递。
  • 它用于移动开发,适用于 android、IOS 和桌面的跨平台 LibGdx。不想仅将 Lambda 用于事件目的。

标签: java events listener


【解决方案1】:

我认为Event API一般应该包括以下内容:

  1. 类型安全,避免转换事件/事件类型类 - 它将使您免于将来出现意外的类转换异常。
  2. 对开发人员友好。 API 应该简短而甜美。您应该能够在 IDE(例如 Intellij IDEA 或 Eclipse)中轻松找到合适的侦听器。类型安全将为您提供。
  3. 添加新事件类型应该简单快捷。
  4. 事件结构应该是可重用的。

这是一种可能的方法:

创建基础事件和更复杂的事件结构。

class BaseEvent {
}

class ComplexEvent extends BaseEvent {
    int importantData;
    public ComplexEvent(int i) {
        importantData = i;
    }
}

EventType创建一个单独的类,使每个事件类型依赖于事件。我们现在可以为我们的事件类型重用现有结构。在不破坏现有代码的情况下添加新事件也很容易。

final class EventType<E> {
    private EventType(){}
    public final static EventType<BaseEvent> SimpleEvent = new EventType<BaseEvent>();
    public final static EventType<BaseEvent> SimpleEvent2 = new EventType<BaseEvent>();
    public final static EventType<ComplexEvent> ComplexEvent1 = new EventType<ComplexEvent>();
    public final static EventType<ComplexEvent> ComplexEvent2 = new EventType<ComplexEvent>();
}

将侦听器创建为通用接口,依赖于事件。

interface Listener<E extends BaseEvent> {
    void handle(E event);
}

EventSource中使用这些类,为指定的事件类型注册监听器。

class EventSource {

    private final Map<EventType, List<Listener<? extends BaseEvent>>> listenersMap = new HashMap<EventType, List<Listener<? extends BaseEvent>>>();

    public <E extends BaseEvent> void addListener(EventType<E> eventType, Listener<E> listener) {
        listeners(eventType).add(listener);
    }

    public <E extends BaseEvent> void fire(EventType<E> eventType, E event) {
        for (Listener listener : listeners(eventType)) {
            listener.handle(event);
        }
    }

    private List<Listener<? extends BaseEvent>> listeners(EventType eventType) {
        if (listenersMap.containsKey(eventType)) {
            return listenersMap.get(eventType);
        } else {
            List<Listener<? extends BaseEvent>> listenersList = new ArrayList();
            listenersMap.put(eventType, listenersList);
            return listenersList;
        }
    }

}

检查 API 的使用,它允许我们在 Listener 的实现中获得正确的事件结构。

public static void main(String[] args) {
        EventSource eventSource = new EventSource();
        eventSource.addListener(EventType.SimpleEvent, new Listener<BaseEvent>() {
            @Override
            public void handle(BaseEvent event) {
                log.info("Simple 1 handled!");
            }
        });
        eventSource.addListener(EventType.SimpleEvent2, new Listener<BaseEvent>() {
            @Override
            public void handle(BaseEvent event) {
                log.info("Simple 2 handled!");
            }
        });
        // compile error! we must handle ComplexEvent type
//        eventSource.addListener(EventType.ComplexEvent1, new Listener<BaseEvent>() {
//            @Override
//            public void handle(BaseEvent event) {
//                log.info("Complex 1 handled!");
//            }
//        });
        eventSource.addListener(EventType.ComplexEvent1, new Listener<ComplexEvent>() {
            @Override
            public void handle(ComplexEvent event) {
                log.info("Complex 1 handled!" + event.importantData);
            }
        });
        eventSource.addListener(EventType.ComplexEvent2, new Listener<ComplexEvent>() {
            @Override
            public void handle(ComplexEvent event) {
                log.info("Complex 2 handled!" + event.importantData);
            }
        });
        eventSource.fire(EventType.SimpleEvent, new BaseEvent());
        eventSource.fire(EventType.SimpleEvent2, new BaseEvent());
        eventSource.fire(EventType.ComplexEvent1, new ComplexEvent(1));
        eventSource.fire(EventType.ComplexEvent2, new ComplexEvent(2));
        // compile error! we must fire ComplexEvent to our listeners
        //eventSource.fire(EventType.ComplexEvent1, new BaseEvent());
    }

当您不需要传递任何特定数据时,您还可以将简写 fire 调用添加到 EventSource

public <E extends BaseEvent> void fire(EventType<E> eventType) {
    fire(eventType, (E) new BaseEvent());
}

并从客户端代码中简单地使用它:

eventSource.fire(EventType.SimpleEvent);

【讨论】:

  • 非常感谢!就是这样!
  • 非常简单 - 非常好。
猜你喜欢
  • 1970-01-01
  • 2015-01-09
  • 2012-05-12
  • 1970-01-01
  • 1970-01-01
  • 2019-12-30
  • 2020-09-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多