Merge pull request #2023 from ktoso/doc-3886-partialfunc-orelse-ktoso
=doc #3886 improved example of PartialFunction.orElse composition
This commit is contained in:
commit
d7283abfcb
2 changed files with 48 additions and 20 deletions
|
|
@ -890,12 +890,19 @@ supervisor’s decision the actor is resumed (as if nothing happened), restarted
|
|||
Extending Actors using PartialFunction chaining
|
||||
===============================================
|
||||
|
||||
A bit advanced but very useful way of defining a base message handler and then
|
||||
extend that, either through inheritance or delegation, is to use
|
||||
``PartialFunction.orElse`` chaining.
|
||||
Sometimes it can be useful to share common behavior among a few actors, or compose one actor's behavior from multiple smaller functions.
|
||||
This is is possible because an actor's :meth:`receive` method returns an ``Actor.Receive``, which is a type alias for ``PartialFunction[Any,Unit]``,
|
||||
and partial functions can be chained together using the ``PartialFunction#orElse`` method. You can chain as many functions you need,
|
||||
however you should keep in mind that "first match" wins - which may be important when combining functions that both can handle the same type of message.
|
||||
|
||||
For example, imagine you have a set of actors which are either ``Producers`` or ``Consumers``, yet sometimes it makes sense to
|
||||
have an actor share both behaviors. This can be easily achieved without having to duplicate code by extracting the behaviors to
|
||||
traits and implementing the actor's :meth:`receive` as combination of these partial functions.
|
||||
|
||||
.. includecode:: code/docs/actor/ActorDocSpec.scala#receive-orElse
|
||||
|
||||
Instead of inheritance the same pattern can be applied via composition - one would simply compose the receive method using partial functions from delegates.
|
||||
|
||||
Initialization patterns
|
||||
=======================
|
||||
|
||||
|
|
|
|||
|
|
@ -13,13 +13,12 @@ import akka.event.Logging
|
|||
//#imports1
|
||||
|
||||
import scala.concurrent.Future
|
||||
import akka.actor.{ ActorRef, ActorSystem, PoisonPill, Terminated }
|
||||
import akka.actor.{ ActorRef, ActorSystem, PoisonPill, Terminated, ActorLogging }
|
||||
import org.scalatest.{ BeforeAndAfterAll, WordSpec }
|
||||
import org.scalatest.Matchers
|
||||
import akka.testkit._
|
||||
import akka.util._
|
||||
import scala.concurrent.duration._
|
||||
import akka.actor.Actor.Receive
|
||||
import scala.concurrent.Await
|
||||
|
||||
//#my-actor
|
||||
|
|
@ -197,25 +196,47 @@ object SwapperApp extends App {
|
|||
|
||||
//#receive-orElse
|
||||
|
||||
abstract class GenericActor extends Actor {
|
||||
// to be defined in subclassing actor
|
||||
def specificMessageHandler: Receive
|
||||
trait ProducerBehavior {
|
||||
this: Actor =>
|
||||
|
||||
// generic message handler
|
||||
def genericMessageHandler: Receive = {
|
||||
case event => printf("generic: %s\n", event)
|
||||
}
|
||||
|
||||
def receive = specificMessageHandler orElse genericMessageHandler
|
||||
}
|
||||
|
||||
class SpecificActor extends GenericActor {
|
||||
def specificMessageHandler = {
|
||||
case event: MyMsg => printf("specific: %s\n", event.subject)
|
||||
val producerBehavior: Receive = {
|
||||
case GiveMeThings =>
|
||||
sender() ! Give("thing")
|
||||
}
|
||||
}
|
||||
|
||||
case class MyMsg(subject: String)
|
||||
trait ConsumerBehavior {
|
||||
this: Actor with ActorLogging =>
|
||||
|
||||
val consumerBehavior: Receive = {
|
||||
case ref: ActorRef =>
|
||||
ref ! GiveMeThings
|
||||
|
||||
case Give(thing) =>
|
||||
log.info("Got a thing! It's {}", thing)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Producer extends Actor with ProducerBehavior {
|
||||
def receive = producerBehavior
|
||||
}
|
||||
|
||||
class Consumer extends Actor with ActorLogging with ConsumerBehavior {
|
||||
def receive = consumerBehavior
|
||||
}
|
||||
|
||||
|
||||
class ProducerConsumer extends Actor with ActorLogging
|
||||
with ProducerBehavior with ConsumerBehavior {
|
||||
|
||||
def receive = producerBehavior orElse consumerBehavior
|
||||
}
|
||||
|
||||
// protocol
|
||||
case object GiveMeThings
|
||||
case class Give(thing: Any)
|
||||
|
||||
//#receive-orElse
|
||||
|
||||
class ActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue