Нужен элегантный способ вызова произвольного кода через заданный интервал

Хорошо, у меня есть игровой сервер, работающий в Java/Hibernate/Spring/Quartz. Игровые часы тикают с помощью кварцевого таймера, и это прекрасно работает.

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

Например, каждые 24 часа игрового времени (~ 47 минут реального времени, в зависимости от множителя часов сервера) происходит множество различных игровых действий один раз в день, таких как пополнение запасов или что-то еще.

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

Прослушиватель сообщений — это неприятная часть — он включает клавишу и нажимает один из своих методов.

Теперь я понимаю, что это может работать очень хорошо, но это действительно не устраивает меня. Каким было бы ваше решение, чтобы каждый фрагмент кода был в своем маленьком классе? Какой шаблон проектирования охватывает это? (уверен, что есть). У меня есть несколько идей, но хотелось бы услышать мнения.


person Jason Maskell    schedule 30.03.2009    source источник


Ответы (5)


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

Узор выглядит примерно так:

private final Map<String, Handler> handlers = new TreeMap<String, Handler>();

public void register(String event, Handler handler) { 
  handlers.put(event, handler); 
}

public void handle(String event) {
  Handler handler = handler.get(event);
  if (handler == null) {
    /* Log or throw an exception for unknown event type. */
  }
  else {
    handler.execute();
  }
}

Вместо того, чтобы явно регистрировать обработчики, вы можете использовать что-то вроде ServiceLoader в Java 6, чтобы добавить новое поведение, просто перетащив JAR-файлы в путь к классу.

person erickson    schedule 30.03.2009
comment
Я не отметил это как ответ, потому что это простой ответ - конечно, сопоставление лучше, чем переключатель, но это действительно не элегантно. ServiceLoader был бы лучшим подходом, но он не касается какой-либо другой уже существующей инфраструктуры, такой как Spring. - person Jason Maskell; 02.04.2009

Я бы использовал вариант Command Pattern. Я бы расширил шаблон Command, чтобы создать класс IIntervalCommand. Он будет иметь свойство interval и свойство CanExecute только для чтения в дополнение к методу Execute.

Затем вы создаете класс CommandList, содержащий список IIntervalCommands. У него будет метод CheckToExecute, который вы передаете ему текущее игровое время. Метод CheckToExecute будет проходить по списку, вызывая CanExecute для каждой команды. CanExecute вернет true, если истечет время. Если CanExecute возвращает значение true, тогда CheckToExecute вызовет метод Execute объекта, реализующего IIntervalCommand.

Затем добавление дополнительных игровых событий — вопрос создания нового класса, реализующего IIntervalClass. Создание объекта и добавление его в IntervalCommandList.

Если обработка события занимает много времени, команда может запустить обработку в виде отдельного потока. Он будет возвращать false своему свойству CanExecute до тех пор, пока поток не вернется, даже если интервал снова прошел. Или у вас он порождает другой поток, если интервал снова прошел.

Вы избегаете гигантского оператора case. Вы можете удалить базу данных и настроить параметры при создании экземпляров объектов. Или сохраните его и используйте как часть фабрики, которая создает все ваши IIntervalCommands.

person RS Conley    schedule 30.03.2009

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

Должно быть возможно иметь что-то вроде:

timerQueue.registerHandler("key",new TimerHandler(){
   // do something timer related  
});

Таким образом, вы можете перезапустить события обработки кода Java, не теряя сохраненную очередь событий.

http://en.wikipedia.org/wiki/Priority_queue'>На приоритетные очереди стоит обратить внимание, если вы еще этого не сделали.

person fuzzy-waffle    schedule 30.03.2009

Лично я бы не стал помещать это в базу данных, а оставил бы отдельную службу, работающую в фоновом режиме. Тогда мой веб-сервис или веб-приложение будет связываться с этой службой через межпроцессное взаимодействие. Не знаю, как это переводится в мир Java.

person Ender    schedule 30.03.2009

Концептуально я думаю, что вы делаете две вещи;

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

DateTime getFutureTime( VirtualTimeSpan timespan)

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

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

Увы, JMS никогда не подходила большинству людей. Многие люди сочли, что он слишком тяжеловесен или его реализации слишком глючны. Поэтому обычно люди прибегали к самодельным технологиям очередей. Но понятия все есть. Нельзя ли просто кварц?

person krosenvold    schedule 30.03.2009