Могу ли я сойти с ума с обработчиками событий? Я иду «неправильный путь» с моим дизайном?

12

Наверное, я решил, что мне действительно нравятся обработчики событий. Возможно, я немного страдаю от паралича анализа, но я обеспокоен тем, чтобы сделать мой дизайн громоздким или столкнуться с какими-то непредвиденными последствиями для моих дизайнерских решений.

Мой игровой движок в настоящее время выполняет базовый спрайт-рендеринг с панорамирующей камерой. Мой дизайн выглядит примерно так:

SceneHandler

Содержит список классов, которые реализуют интерфейс SceneListener (в настоящее время только Sprites). Вызывает render () один раз за такт и отправляет onCameraUpdate (); сообщения для SceneListeners.

InputHandler

Опрашивает ввод один раз за тик и отправляет простое сообщение «onKeyPressed» в InputListeners. У меня есть Camera InputListener, который содержит экземпляр SceneHandler и запускает updateCamera (); события, основанные на том, что вход.

AgentHandler

Вызывает действия по умолчанию для любых Агентов (AI) один раз за такт и проверяет стек на наличие новых зарегистрированных событий, отправляя их определенным агентам по мере необходимости.

Итак, у меня есть базовые спрайтовые объекты, которые могут перемещаться по сцене и использовать элементарные способы управления рулем для путешествий. Я попал на обнаружение столкновений, и именно здесь я не уверен, что направление, в котором движется мой дизайн, хорошее. Это хорошая практика иметь много маленьких обработчиков событий? Я представляю себе, что я должен реализовать что-то вроде CollisionHandler.

Будет ли мне лучше с более консолидированным EntityHandler, который обрабатывает AI, обновления столкновений и другие взаимодействия сущностей в одном классе? Или я буду в порядке, просто внедрив множество различных подсистем обработки событий, которые передают сообщения друг другу в зависимости от того, что это за событие? Должен ли я написать EntityHandler, который просто отвечает за координацию всех этих обработчиков событий?

Я понимаю, что в некоторых случаях, таких как мой InputHandler и SceneHandler, это очень специфические типы событий. Большая часть моего игрового кода не будет заботиться о вводе, а большая часть не будет заботиться об обновлениях, которые происходят исключительно при рендеринге сцены. Таким образом, я чувствую, что моя изоляция этих систем оправдана. Тем не менее, я задаю этот вопрос специально для событий типа игровой логики.

sensae
источник

Ответы:

21

Проектирование на основе событий в основном включает реализацию шаблона проектирования Reactor .

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

Первая и главная из проблем заключается в том, что обработчики должны быстро возвращаться , поскольку каждый, кто проделал серьезную работу над платформами на основе GUI и приложениями на основе javascript, хорошо знает.

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

В этих случаях вы можете различить две ситуации (не обязательно взаимоисключающие):

Комплексные обязанности, связанные с событиями, и сложные обязанности, не связанные с событиями .

Комплексные обязанности, связанные с событиями:

В первом случае вы объединили слишком много обязанностей в один компонентный обработчик : слишком много действий предпринято в ответ на событие или, например, выполняются синхронизации.

В этом случае решение состоит в том, чтобы разделить обработчик на разные обработчики (в разных объектах, если необходимо) и позволить дескриптору инициировать события вместо непосредственного вызова кода обработки. Это позволит вам разрешить управление событием с более высоким приоритетом до того, как ваш основной обработчик вернулся после добавления событий в очередь событий.

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

Комплексные не связанные с событием обязанности:

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

Здесь шаблон Reactor в основном терпит неудачу , стоит немного разбить алгоритм генерации Фибоначчи на маленькие порции, которые запускают события после завершения, чтобы инициировать следующий шаг.

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

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

Когда обработчик должен запустить длинное действие, он инкапсулирует это действие в блок задания, чтобы поместить его в очередь заданий. Это инкапсулированное действие должно иметь средство для запуска событий в потоке реактора. Администратор очередей заданий может работать в своем собственном потоке или в потоке реактора и реагировать на событие «newJob».

Менеджер очереди заданий выполняет следующие действия:

  • он выделяет единицу работы потоку из пула гибких потоков, используя свой алгоритм планирования, если пул может предоставить один поток (имеется свободный поток или разрешено создание нового потока)

  • прослушайте сам пул, чтобы увидеть, завершен ли блок задания, чтобы можно было восстановить поток для выполнения еще одного ожидающего модуля, если таковой имеется.

Используя механизм запуска событий, задание может инициировать события для уведомления о завершении, состоянии обновления, предупреждениях, ошибках или всякий раз, когда это необходимо. Гибкий пул потоков обеспечивает хорошее использование ресурсов, а также позволяет избежать зависания всей системы; очередь заданий позволяет вам выбрать, какой тип приоритета назначать различным видам действий, поскольку вы знаете, что они потребуют согласованного количества вычислительных ресурсов.

FXIII
источник
3
+1 за очень тщательный Несколько ссылок для любопытного читателя было бы здорово.
Сам Хочевар
1

В большинстве ситуаций использование событий поверх других параметров часто может улучшить скорость и децентрализацию. Я думаю, что если вы можете использовать много событий, вы должны пойти на это.

annonymously
источник