Подтвердить что ты не робот

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

Я пытаюсь реализовать признак Pub/Sub для смешивания с другими аккскими актерами с использованием стекируемого признака.

Вот что я придумал:

trait PubSubActor extends Actor {
  abstract override def receive = 
    super.receive orElse {
      case Subscribe(topic) => /* ... */
      case Publish(topic, msg) => /* ... */
    }
}

class MyActor extends Actor with PubSubActor {
  override def receive = {
    case SomeMessage(a, b, c) => /* ... */
  }
}

В этот момент компилятор возвращает ошибку: ошибка: переопределяющий метод получения в признаке метода MyActor... get требует модификаторов абстрактного переопределения.

Можете ли вы объяснить мне, почему это не работает? Как я могу исправить это, чтобы он работал?

Спасибо!

UPDATE

Следующие работы:

trait PubSubActor extends Actor {
  abstract override def receive = 
    super.receive orElse {
      case Subscribe(topic) => /* ... */
      case Publish(topic, msg) => /* ... */
    }
}

class MyActor extends Actor {
  override def receive = {
    case SomeMessage(a, b, c) => /* ... */
  }
}

class MyActorImpl extends MyActor with PubSubActor

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

4b9b3361

Ответ 1

Там простое и краткое решение:

Определите признак приема, который связывает несколько функций приема с помощью orElse:

trait Receiving { 
  var receivers: Receive = Actor.emptyBehavior 
  def receiver(next: Actor.Receive) { receivers = receivers orElse next }
  def receive = receivers // Actor.receive definition
}

Использование этого в актерах легко:

trait PubSubActor extends Receiving {
  receiver {
    case Publish => /* I'm the first to handle messages */
  }
}

class MyActor extends PubSubActor with Receiving {
  receiver {
    case SomeMessage => /* PubSubActor didn't handle, I receive the message */ 
  }
}

Будет вызван первый прием PubSubActor. Если сообщение не было обработано, оно будет передано в приемник MyActor.

Ответ 2

Вы, безусловно, можете добиться того, что ищете, используя функцию Akka composable actor. Это описано немного в Расширение участников с использованием цепочки PartialFunction.

Сначала, код инфраструктуры (прямо из документов):

class PartialFunctionBuilder[A, B] {
  import scala.collection.immutable.Vector

  // Abbreviate to make code fit
  type PF = PartialFunction[A, B]

  private var pfsOption: Option[Vector[PF]] = Some(Vector.empty)

  private def mapPfs[C](f: Vector[PF] => (Option[Vector[PF]], C)): C = {
    pfsOption.fold(throw new IllegalStateException("Already built"))(f) match {
      case (newPfsOption, result) => {
        pfsOption = newPfsOption
        result
      }
    }
  }

  def +=(pf: PF): Unit =
    mapPfs { case pfs => (Some(pfs :+ pf), ()) }

  def result(): PF =
    mapPfs { case pfs => (None, pfs.foldLeft[PF](Map.empty) { _ orElse _ }) }
}

trait ComposableActor extends Actor {
  protected lazy val receiveBuilder = new PartialFunctionBuilder[Any, Unit]
  final def receive = receiveBuilder.result()
}

Затем поведение, которое вы хотите создать для актеров:

trait PubSubActor { self:ComposableActor =>
  receiveBuilder += {
    case Subscribe(topic) => /* ... */
    case Publish(topic, msg) => /* ... */
  }
}

trait MyActor  { self:ComposableActor =>
  receiveBuilder += {
    case SomeMessage(a, b, c) => /* ... */
  }
}

И, наконец, фактический актер, который использует эти композиции:

class MyActorImpl extends ComposableActor with PubSubActor with MyActor

Ответ 3

Попробуйте наоборот:

object Subscription {
  case object Subscribe
  case object Unsubscribe
}

trait Subscription {
  this: Actor =>

  import Subscription._

  var subscribers = Set.empty[ActorRef]

  def receive: Receive = {
    case Subscribe => subscribers += sender
    case Unsubscribe => subscribers -= sender
  }
}

class MyActor extends Actor with Subscription {
  def receive = super.receive orElse {
     case msg => // handle msg
  }
}

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

class Core extends Actor {
  def receive = Actor.emptyBehavior
}

class MyActor extends Core with Subscription

Кстати, вы можете больше узнать о шаблоне (не относящемся к Актерам) здесь.

Ответ 4

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

class MyActor extends Actor with PubSubActor {
  override def receive = {
    case SomeMessage(a, b, c) => /* ... */
 }}

это не сделано

Причина в том, что компилятор scala делает линеаризацию для наследования поэтому в цепочке методов приема мы имеем следующую последовательность:

 1)  override def receive = {
        case SomeMessage(a, b, c) => /* ... */
      }
 2) abstract override def receive = super.receive orElse {
        case Subscribe(topic) => /* ... */
        case Publish(topic, msg) => /* ... */
      }

 3) then Actor.receive - it hasn't an implementation

Так что PubSubActor.receive нельзя вызвать, потому что он использует super.receive, в свою очередь super.receive полагается на Actor.receive, но Actor.receive не имеет реализации.

Во второй конструкции

class MyActor extends Actor {
  override def receive = {
    case SomeMessage(a, b, c) => /* ... */
  }}

class MyActorImpl extends MyActor with PubSubActor

мы получаем цепочку методов

1)

abstract override def receive = super.receive orElse {
   case Subscribe(topic) => /* ... */
   case Publish(topic, msg) => /* ... */
}

2)

override def receive = {
   case SomeMessage(a, b, c) => /* ... */
}

3), то Actor.receive - у него нет реализации

Итак, PubSubActor.receive может успешно вызвать super.receive

Дополнительная информация:

Штабелируемые черты

Scala спецификация языка, см. 5.1.2