如何使用Akka演员使用可堆叠的特质模式?

时间:2013-08-08 11:17:41

标签: scala akka traits

我正在尝试使用可堆叠特征将Pub / Sub特性混合到其他akka actor中。

以下是我提出的建议:

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 ...方法接收需要`abstract override'修饰符。

你能解释一下为什么这不起作用吗?我怎样才能修复它以便它起作用?

谢谢!

更新

以下作品:

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

但为什么呢?为什么我能以这种方式获得我想要的行为而不是另一种?有什么原因?我似乎无法弄清楚这两个样本之间的根本区别是什么。

4 个答案:

答案 0 :(得分:17)

有一个简单而简洁的解决方案:

定义一个使用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的接收。

答案 1 :(得分:9)

使用Akka的可组合演员功能,你当然可以实现你想要的。这在Extending Actors using PartialFunction chaining中有所描述。

首先,基础设施代码(直接来自文档):

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

答案 2 :(得分:2)

反过来试试:

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

顺便说一下,你可以阅读更多关于模式的信息(与演员无关)here

答案 3 :(得分:1)

首先,请原谅我的英语  我认为重点是抽象覆盖修饰符需要存在接收方法的具体实现,但在第一个构造中

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

没有完成

原因是scala编译器为继承做了线性化 所以在receive的方法链中我们有以下顺序:

 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

其他信息:

Stackable traits

Scala language specification, see 5.1.2