stacking multiple traits in akka Actors

2019-03-15 16:58发布

问题:

I'm creating multiple traits which extend Actor. Then I want to create an actor class which uses some of these traits. However, I'm not sure how to combine the receive methods from all traits in the receive method of the Actor class.

Traits:

 trait ServerLocatorTrait extends Actor {
    def receive() = {
      case "s" => println("I'm server ")
    }
  }

  trait ServiceRegistrationTrait extends Actor {
    def receive() = {
      case "r" => println("I'm registration ")
    }
  }

The Actor:

class FinalActor extends Actor with ServiceRegistrationTrait with ServerLocatorTrait {
  override def receive = {
     super.receive orElse ??? <--- what to put here
  }
}

Now if I send "r" and "s" to FinalActor it goes only in ServerLocatorTrait - which is the last trait added. So the way this works right now is that it considers super the last trait added, so in this case ServerLocatorTrait

Question:
How do I combine the receive methods from all the traits in FinalActor?

PS - I've seen the actors with react example: http://www.kotancode.com/2011/07/19/traits-multiple-inheritance-and-actors-in-scala/ but it's not what I need

回答1:

I'm not sure if you can combine the receive methods, since that would involve calling the super's super to obtain the ServiceRegistration's receive method. It would also be very confusing.

Another way would be to give different names to the receive method in the traits.

trait ServerLocatorTrait extends Actor {
  def handleLocation: Receive = {
    case "s" => println("I'm server ")
  }
}

trait ServiceRegistrationTrait extends Actor {
  def handleRegistration: Receive = {
    case "r" => println("I'm registration ")
  }
}

class FinalActor extends Actor with ServiceRegistrationTrait with ServerLocatorTrait {
  def receive = handleLocation orElse handleRegistration
}

object Main extends App {

  val sys = ActorSystem()

  val actor = sys.actorOf(Props(new FinalActor))

  actor ! "s"
  actor ! "r"

  sys.shutdown()

}

You can still use you initial approach, but you must chain the super.receive for each mixed trait.

trait IgnoreAll extends Actor {
  def receive: Receive = Map()
}

trait ServerLocatorTrait extends Actor {
  abstract override def receive = ({
    case "s" => println("I'm server ")
  }: Receive) orElse super.receive
}

trait ServiceRegistrationTrait extends Actor {
  abstract override def receive = ({
    case "r" => println("I'm registration ")
  }: Receive) orElse super.receive
}

class FinalActor extends IgnoreAll with ServiceRegistrationTrait with ServerLocatorTrait

The latter solution looks pretty ugly to me.

Please see the below link for a more detailed discussion on the subject:

Extending Actors using PartialFunction chaining