Как проектировать актеров со статусом в чистом виде FP - PullRequest
0 голосов
/ 26 июня 2018

У меня есть задача: при появлении сообщения «userStart» запустить таймер на 5 секунд, если пользователь отправит ответ раньше таймера -> отменить таймер.Код прост и работает, вопрос заключается в том, как сделать это в FP.Насколько я понимаю, я должен использовать «val» вместо «var».Я новичок в FP, поэтому я был бы рад, если бы кто-то мог помочь мне с этим, или порекомендовать некоторые источники, где я могу найти простые примеры, как делать такие вещи.Спасибо!

class Game extends Actor{

  var actsCount:Int = 0
  var timer:Option[Cancellable] = None

  def startTimer(): Unit = {
    timer = Some(context.system.scheduler.scheduleOnce(5 seconds, self, "userMissed"))
  }

  def receive = {
    case "userStart" => startTimer()
      sender() ! "do move"
    case "userAct" =>
      println("> user made his move")
      actsCount += 1
      if(timer.isDefined)
        timer.get.cancel()
    case "userMissed" => println("> user missed his move")
  }
}

Ответы [ 2 ]

0 голосов
/ 26 июня 2018

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

Однако, если вы все еще не удовлетворены своим дизайном, есть несколько вариантов:

Конечные автоматы

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

Стать / Unbecome

Актеры также имеют возможность изменять свой receive метод на основе входящих сообщений. Ваша логика синхронизации может быть встроена в логику становления и непринятия.

0 голосов
/ 26 июня 2018

Вот версия, использующая become для отслеживания состояния в функции приема:

class Game extends Actor{     
  def startTimer(): Cancellable = context.system.scheduler.scheduleOnce(5 seconds, self, "userMissed")

  def receive = idleReceive(0)

  def idleReceive(actsCount: Int): Actor.Receive = {
    case "userStart" => startTimer()
      context.become(waitingReceive(actsCount, startTimer()))
      sender() ! "do move"
  }

  def waitingReceive(actsCount: Int, timer: Cancellable): Actor.Receive = {
    case "userAct" =>
      println("> user made his move")
      context.become(idleReceive(actsCount + 1))
      timer.cancel()
    case "userMissed" =>
      println("> user missed his move")
      context.become(idleReceive(actsCount))
  }
}

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

...