在Akka接收方法中组合Scala特性行为

20

考虑这两个特点:

trait Poked extends Actor {
  override def receive = {
    case Poke(port, x) => ReceivePoke(port, x)
  }

  def ReceivePoke(port: String, x: Any)
}

trait Peeked extends Actor {
  override def receive = {
    case Peek(port) => ReceivePeek(port)
  }

  def ReceivePeek(port: String)
}

现在考虑我可以创建一个实现这两个特质的新Actor:

val peekedpoked = actorRef(new Actor extends Poked with Peeked)

如何组合接收处理程序?即,接收器应该像以下代码一样,“自动生成”(即,所有特性应该组合):

def receive = (Poked.receive: Receive) orElse (Peeked.receive: Receive) orElse ...
2个回答

28
您可以使用super[T]引用特定超类/特质的成员。 例如:
trait IntActor extends Actor {
    def receive = {
        case i: Int => println("Int!")
    }
}

trait StringActor extends Actor {
    def receive = {
        case s: String => println("String!")
    }
}

class IntOrString extends Actor with IntActor with StringActor {
    override def receive = super[IntActor].receive orElse super[StringActor].receive
}

val a = actorOf[IntOrString].start
a ! 5 //prints Int!
a ! "Hello" //prints String!

编辑:

针对Hugo的评论,这里提供一种解决方案,可以让您组合mixin而不必手动连接它们的接收器。基本上,它涉及一个具有可变List [Receive]的基础特质,每个混入的特质调用一个方法将其自己的接收器添加到列表中。

trait ComposableActor extends Actor {
  private var receives: List[Receive] = List()
  protected def registerReceive(receive: Receive) {
    receives = receive :: receives
  }

  def receive = receives reduce {_ orElse _}
}

trait IntActor extends ComposableActor {
  registerReceive {
    case i: Int => println("Int!")
  }
}

trait StringActor extends ComposableActor {
  registerReceive {
    case s: String => println("String!")
  }
}

val a = actorOf(new ComposableActor with IntActor with StringActor).start
a ! 5 //prints Int!
a ! "test" //prints String!

唯一需要记住的是接收顺序不应该很重要,因为你无法轻易地预测哪一个是链中的第一个,但你可以通过使用可变哈希表而不是列表来解决这个问题。


这非常有趣,谢谢 :-) 但它假设了存在一种类型 IntOrString,既可以是一个整数又可以是一个字符串,并且 IntOrString 知道它应该组合它们(如果我正在构建一个框架,其他人可能会忽视)。难道不能自动地让 IntActor 和 StringActor traits 组合吗? - Hugo Sereno Ferreira
3
通过混合特征的线性化来给出顺序,因此是“可预测的”;-) 并且使用前置匹配可以覆盖后面的特征而不影响前面的特征,所以我认为你的解决方案非常好! - Roland Kuhn
你的 Scala 技能展现得非常出色! :-) - Hugo Sereno Ferreira

5

您可以在基础actor类中使用空的Receive,并在其定义中链接接收器。 Akka 2.0-M2的示例:

import akka.actor.Actor
import akka.actor.Props
import akka.event.Logging
import akka.actor.ActorSystem

class Logger extends Actor {
  val log = Logging(context.system, this)

  override def receive = new Receive {
    def apply(any: Any) = {}
    def isDefinedAt(any: Any) = false
  }
}

trait Errors extends Logger {
  override def receive = super.receive orElse {
    case "error" => log.info("received error")
  }
}

trait Warns extends Logger {
  override def receive = super.receive orElse {
    case "warn" => log.info("received warn")
  }
}

object Main extends App {
  val system = ActorSystem("mysystem")
  val actor = system.actorOf(Props(new Logger with Errors with Warns), name = "logger")
  actor ! "error"
  actor ! "warn"
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接