需求和想法
- 每个监听器都注册到一个预定义(编译时)的1个或多个事件类型。
- 监听器可以在运行时注册和注销。
- 注册侦听器的顺序必须保持,因为它是它们接收事件的顺序(侦听器始终添加到末尾,但可以从任何位置删除)。
- 事件类型可以有0个或更多个已注册的监听器随时接收它。
这种关系的可视化可以解释为一个表:
| Listener1 | Listener2 | Listener3 | Listner5
-----------------------------------------------------
Event1 | X | | X | X
-----------------------------------------------------
Event2 | | X | X | X
-----------------------------------------------------
Event3 | | | |
-----------------------------------------------------
Event4 | | | | X
-----------------------------------------------------
- 行的顺序不重要。
- 列的顺序必须保持不变。
- 可以添加和删除列。
- 在运行时,行数是恒定的。
我的想法是:
- 在分派事件时,我需要知道已注册接收事件类型的侦听器。 我考虑使用
Event -> Collection<Listener>
映射,其中键无序。 侦听器的集合需要保持插入顺序(如果接下来的点可以用于此,则无需保持顺序)。 - 我需要保留一个侦听器集合,该集合始终保持插入顺序,而不管它们所注册的事件如何(这个需求来自侦听器所附加到的对象)。 因为即使上面的
Collection<Listener>
是插入有序的,它是根据事件类型而不是全局的。 我正在考虑一个OrderedCollection<Listener>
。 - 当取消注册时,我需要找到侦听器已经注册的所有事件类型,以便可以删除它。 我正在考虑一个
Listener -> Collection<Event>
,除非第一点中的映射可以对值位置的列表执行removeAll(Listener)
操作,但这可能会留下空列表,而不是完全删除键。
对我来说,双向多映射似乎适合这种情况。 支持多重映射的背景是 UnorderedMap<Event, OrderedCollection<Listener>>
和 OrderedMap<Listener, UnorderedCollection<Event>>
。 如果可以使用OrderedMap
中的排序,则可能不需要对OrderedCollection<Listener>
进行排序。 显然,任何无序数据结构都可以排序,但是没有必要。
或者,我看到单个多重映射的想法,其中包含一个反向/反转操作以获取相反的映射。 我的关注点有两个:
- 我不知道一侧是否可以按顺序排序,而另一侧则不是。
- 这似乎是一项昂贵的操作,如果我需要反转映射所有时间,它可能效率低下。
伪代码用法
注册侦听器:
// somewhere
Listener1 listener = new Listener1();
listener.register();
// class definition
class Listener1 extends AbstractListener {
void register() {
DataStructure.put(Event1.class, this);
DataStructure.put(Event2.class, this);
// or
// some collection
DataStructure.put(this, [Event1.class, Event2.class])
}
}
触发事件:
// somewhere
EventManager.dispatch(new Event2());
// class definition
class EventManager {
static void dispatch(Event event) {
OrderedCollection<Listener> listeners = DataStructure.get(event);
listeners.forEach(l -> l.processEvent(event)); // forEach maintains the order
}
}
注销监听器:
Listener2 listener = ...; // got it from somewhere
DataStructure.remove(listener); // remove from everywhere and if a key is left with an empty list remove that key
最终细节和问题
如果有关系的话,大约有30种事件类型和O(10)个并发注册的监听器,尽管在程序的生命周期内可能会有O(100)个监听器 - 其中大部分会被注销和GC'd。
关于排序的说明。由于监听器具有增量唯一的int id
字段,因此我可以通过id号码的排序来确保与注册(插入)顺序相同。目前的差异在于id是在初始化侦听器时设置的,而注册是之后完成的(在这段时间内可能会创建并注册另一个侦听器)。如果在这种情况下使用排序集合优于有序集合,则我可以付出一些努力来确保按id排序等同于按插入顺序排序。
哪种数据结构适合我的需求? 我需要编写自己的(我宁愿不要重复造轮子)还是已经实现了其中一个(Guava,我在看着你)?
SomeTypeEvent
,我应该访问哪个单元格? - user1803551removeListener
。这是“我的想法”中的第3点。 - user1803551