(если говорить с точки зрения разработчика MMO)
Я обнаружил, что ради масштабируемости вы можете захотеть обрабатывать обработку событий в более чем одном потоке;У меня обычно был такой шаблон:
public class Event { EventType getType (); }
// extend Event for special cases that require additional attributes…
public class EventDispatcher {
Map<Class<? extends Event>, List<Observer>> observers;
void send (final Event e) {
for (Observer o : observers.get(e.getClass())) {
o.receiveEvent (e);
}
}
}
Или вы можете использовать enum
:
public enum EventType { … } ;
…
Map <EventType,List<Observer>> observers;
…
for (Observer o : observers.get(e.getEventType ())) …
По мере того, как вы будете справляться с большим количеством наблюдателей, вам может понадобитьсяразбить обработку событий и / или заставить ее запускаться в других потоках.Метод EventDispatcher.send(Event)
дает вам одно место для его расширения, например, вставляя пространственные разделы (вы не можете обнаружить это событие, потому что вы слишком далеко), или вставляя объекты Event в очередь для асинхронной обработки в другом потоке.(s), где обработчик (и) событий не будет влиять на поток, генерирующий событие.
Мы первоначально начали использовать эту модель для обработки распространения событий обратно к игрокам (так что сетевой код выполнялся синхронно насвоего собственного потока), но обнаружил, что система очередей помогла ускорить распространение ИИ и физики.
Недостатком, возможно, является то, что вам необходимо иметь фильтр switch / case в классе Observer (es).).Как правило, это относится к общему базовому классу, если предположить, что у вас есть достаточно небольшое число типов EventTypes для перечисления.
На более ранних итерациях ядра Romance MMO (и все еще поддерживается в базе кода 1.1 для обратной совместимости)Мы использовали интерфейсы для получения различных событий.(Базовый класс подключает их к общим получателям событий - которые мы называем действиями, чтобы отличать их от обработчиков событий пользовательского интерфейса.)
public interface EventFooHandler {
public void handleEventFoo (int bar, int baz);
};
public class SomeObject implements EventFooHandler …
Возможно, эти интерфейсы были тем, что вы имели в виду под «тонной пустых классов?». Этокак правило, «разговорный Java правильный способ сделать это» для вещей, которые работают в вашем собственном стеке / потоке;Например, я считаю, что Swing широко использует «функции обратного вызова» таким же образом, как и любое количество других стандартных библиотек Java.
Конечно, если вы не хотите просто реализовывать интерфейсы на каждом Observer, выможно использовать анонимные классы…
getEventDispatcher ().registerForEventFoo
( new EventFooHandler {
public void handleEventFoo (int bar, int baz) {
this.doGribble (bar + 2, baz);
}});
Если вы не кусаете пулю и не погружаетесь в системы Entity (я уверен, что кто-то обязательно заскочит и подключит один, возможно, упомянув радости неизменного состояния и50% шансов, что они рекомендуют перейти на Erlang для вашего следующего проекта :-)), я бы склонялся к чему-то похожему на первый.Система «единообразного события» позволяет «ортогонально» быть ортогональной, маршрутизация события → наблюдатель может быть изменена в одном месте и предотвращает распространение разных методов на наблюдателей для каждого типа события, с которым вы сталкиваетесь с течением времени.