在软件开发过程中,触发事件并对事件做出反应是非常重要的。
任何按键操作都是一个事件,任何帧渲染都是一个事件等待。
在这里,我将向大家展示如何在Java中创建自己的事件处理。
如果您熟悉设计模式,则很可能听说过观察者模式。
这是任何基于事件的框架的基础,它由一个维护观察者列表的主题组成,并且每当发生某项操作/事件时,它都会通知观察者。
我们将使用从观察者模式派生的更健壮和复杂的系统。即使代码更加复杂,并且我们拥有更多的类并可以更好地控制发生的事情,但总体思路是相同的。
现在,就开始本文的正式讲解吧!
Java中的事件类型
首先,我们需要一些事件类型。
可以将它们简单地标识为字符串,但是要使更简洁的代码,并确保我们不会提交无效的类型,最好使用接口“ EventType”。接口中不需要任何方法。
在为事件注册侦听器时,我们只需要它作为类型约束。
实际的事件类型将是实现我们特定接口的Enums。
请记住,枚举不能扩展,但是它们可以实现一个接口。对于我们的示例,我将仅创建一个具有三个值的LoggerEvent:INFO,WARNING和ERROR。
publicinterface EventType {
}
publicenum LoggerEvent implements EventType {
ERROR,
WARNING,
INFO;
}
创建事件
重要的是,当一个事件被触发时,我们要获取我们需要的信息。通常,我们需要事件的来源、事件类型和事件本身的附加数据。现在,让我们考虑以下事件类。
publicclass Event {
private EventType type;
private EventDispatcher source;
private Object[] eventData;
public Event(EventType type, EventDispatcher source, Object... eventData) {
this.type = type;
this.source = source;
this.eventData = eventData;
}
}
事件调度
我们需要克服的最大障碍是将事件发送给任何监听器,在这里,我将用一个EventDispatcher类来完成这项工作。
它将保留已注册的监听器,可以将事件分派给所有监听器,并且将构建Event对象。
这里,需要考虑一些问题。
首先,请确保每个监听器只注册一次。由于编程错误,不知道代码或简单的错误,可能会出现同一监听器注册两次的情况。注册之前,请检查是否已添加监听器。
接下来,错误处理。调度事件时,监听器可能引发异常。如果处理不当,将使迭代中断已注册的监听器,并且并非所有人都将接收该事件。这可能是由于外部原因,代码中的错误或事件数据的不正确处理而发生的。
publicclass EventDispatcher {
private Map<EventType, List<EventListener>> listeners;
public EventDispatcher() {
this.listeners = new HashMap<>();
}
public void addEventListener(EventType eventType, EventListener listener) {
if (listener == null) return;
List<EventListener> handlersForEventType = listeners.get(eventType);
if (handlersForEventType == null) {
handlersForEventType = new ArrayList<>();
listeners.put(eventType, handlersForEventType);
}
if (handlersForEventType.contains(listener)) return;
handlersForEventType.add(listener);
}
protected void dispatchEvent(EventType eventType, Object... eventData) {
List<EventListener> listenersForEvent = listeners.get(eventType);
if (listenersForEvent == null || listenersForEvent.isEmpty()) return;
listenersForEvent.forEach(listener -> {
try {
listener.onEvent(buildEvent(eventType, eventData));
} catch (Exception e) {
System.err.println("Listener threw exception for event " + eventType);
}
});
}
protected Event buildEvent(EventType eventType, Object... eventData) {
returnnew Event(eventType, this, eventData);
}
}
你可能已经注意到,该map中包含EventListeners列表,这些是将接收事件的对象。
另外,EventListener只是一个接口,它有一个onEvent()方法。
publicinterface EventListener {
void onEvent(Event event);
}
需要说明一下,在当前的实现中,直到前面的监听器完成他们的处理之前,监听器不会收到事件。
严格来说,这不是正确的做法。调度器应该做到,一旦事件被发送,就继续下一个监听器。为了简单起见,我不会在这里处理这种情况。
汇总
现在我们已经具备了触发和处理事件所需的所有类,接下来就来创建一个logger。
为简单起见,logger将只有3种方法可以打印收到的消息。这3种方法分别用于处理错误,警告和信息消息。
publicclass MyCustomLogger extends EventDispatcher {
public void error(String message) {
System.err.println("ERROR: " + message);
dispatchEvent(LoggerEventType.ERROR, message);
}
public void warning(String message) {
System.out.println("WARN: " + message);
dispatchEvent(LoggerEventType.WARNING, message);
}
public void info(String message) {
System.out.println("INFO: " + message);
dispatchEvent(LoggerEventType.INFO, message);
}
}
这种方法的好处是,我们已经拥有了注册一个监听器和调度已经写好的事件所需的所有功能。
现在,我们只要创建我们的Listener就可以了。
在这个例子中,我假设我们希望每当有错误被记录时,就会发送一封邮件,而且邮件中应该包含错误信息。
所以,我们就有了这个简单的监听器。
publicclass ErrorEmailSender implements EventListener {
@Override
public void onEvent(Event event) {
sendEmail((String) event.getEventData()[0]);
}
private void sendEmail(String errorMessage) {
System.out.println("Sending email with: " + errorMessage);
}
}
那么,还有一个疑问–“当要处理事件时,是否需要创建一个新的类?”
答案是不需要,你可以使用lambdas来实现这一点,而不需要一个新的类。
public static void main(String[] argv) {
MyCustomLogger logger = new MyCustomLogger();
logger.addEventListener(LoggerEventType.ERROR, new ErrorEmailSender());
logger.addEventListener(LoggerEventType.INFO, l -> {System.out.println("From Warning Listener: " + l.getEventData()[0]);});
logger.info("Only from logger will show");
logger.warning("A warning message");
logger.error("An error message");
}
到这里,就已经完成了Java中事件的创建、调度,可以用于监听项目中的各种事件了。