我正在尝试使用可堆叠特征将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
但为什么呢?为什么我能以这种方式获得我想要的行为而不是另一种?有什么原因?我似乎无法弄清楚这两个样本之间的根本区别是什么。
答案 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
其他信息: