Как избежать мутации переменной состояния в акторе Akka с унаследованным поведением?

У меня есть несколько актеров Akka с обычным поведением. Это общее поведение определено в свойстве:

trait CommonBehavior {
  this: Actor =>
  var history: List[String] = Nil
  protected def commonActions: Receive = {
    case Action1 => history = "action1" :: history.take(99)
    case Action2 => history = "action2" :: history.take(99)
    case GetHistory => sender() ! history
  }
}

Актеры переопределяют эту черту и определяют дополнительное поведение с помощью orElse. Вот один из примеров такого актера:

class MyActor extends Actor with CommonBehavior {
  var state: Int = 0
  override def receive: Receive =
    commonActions orElse {
      case Increment => state += 1
      case Decrement => state -= 1
    }
}

Я знаю, что изменение состояния является антипаттерном, и я хочу провести его рефакторинг с использованием context.become. Проблема в том, что при изменении состояния в MyActor на context.become я не знаю параметр для commonActions. Возможно ли вообще наследовать поведение? Нужен ли мне более масштабный рефакторинг (например, создание прокси-актера)? Вот как далеко я продвинулся:

trait CommonBehavior {
  this: Actor =>
  protected def commonActions(history: List[String]): Receive = {
    case Action1 => context.become(??? orElse commonActions("action1" :: history.take(99))
    case Action2 => context.become(??? orElse commonActions("action2" :: history.take(99))
    case GetHistory => sender() ! history
  }
}

class MyActor extends Actor with CommonBehavior {
    override def receive = ready(0)
  def ready(state: Int): Receive = {
    case Increment => context.become(ready(state + 1) orElse commonActions(???))
    case Decrement => context.become(ready(state - 1) orElse commonActions(???))
  } orElse commonActions(Nil)
}

person mirelon    schedule 22.01.2020    source источник
comment
Взгляните на неизменное поведение Akka Type. Может быть, это лучший и элегантный способ решения вашей проблемы.   -  person Emiliano Martinez    schedule 22.01.2020


Ответы (2)


Изменение состояния в акторе не является антипаттерном, вполне нормально делать больше акторов в стиле OO (и может быть более производительным в случаях с высокой пропускной способностью), которые изменяют состояние в ответ на сообщения. Решение сделать больше в стиле FP — это личное предпочтение, поэтому взвесьте плюсы и минусы для вашего варианта использования (ваш опыт, опыт команды, размер проекта и т. д.), а не догматически следуйте чьему-то мнению. Особенно, если у вас уже есть иерархия классов, которая требует от вас мутаций, а не смены поведения.

Если вы решите, что хотите сделать актера в стиле FP, я бы порекомендовал пересмотреть всю структуру и не иметь миксин с изменяемым состоянием, требующий смешивания с Actor для начала, но проектировать с этой точки зрения. Я бы также рекомендовал использовать новые типизированные API, поскольку их сторона «FP:y» дает гораздо более приятный опыт, чем использование become с классическими API-интерфейсами актеров.

person johanandren    schedule 23.01.2020

Насколько я понял, вы хотите избежать мутации состояния внутри актора, изменив контекст актора. Итак, есть 2 возможности:

  1. Если вы боитесь мутировать состояние в акторе, вы можете пойти с этим, поскольку мутация в акторе в порядке и не приводит к проблемам параллелизма. Потому что актеры работают синхронно.

  2. Если вы по-прежнему хотите сохранить неизменное состояние (val) и разрешить изменение состояния во время выполнения, вы можете сделать это с помощью команды be. Если вы не знаете параметры для commonActions, вы можете либо инициализировать его значением по умолчанию, либо выбрать значение из хранилища в зависимости от бизнес-логики, а затем соответствующим образом обновить.

person Girish Bharti    schedule 22.01.2020
comment
С этим ответом много проблем, не в последнюю очередь из-за того, что вопрос явно исключает ваш ответ 1 и дает ссылку, чтобы объяснить, почему. - person Tim; 22.01.2020