Merge pull request #2023 from ktoso/doc-3886-partialfunc-orelse-ktoso

=doc #3886 improved example of PartialFunction.orElse composition
This commit is contained in:
Roland Kuhn 2014-02-27 09:58:19 +01:00
commit d7283abfcb
2 changed files with 48 additions and 20 deletions

View file

@ -890,12 +890,19 @@ supervisors decision the actor is resumed (as if nothing happened), restarted
Extending Actors using PartialFunction chaining Extending Actors using PartialFunction chaining
=============================================== ===============================================
A bit advanced but very useful way of defining a base message handler and then Sometimes it can be useful to share common behavior among a few actors, or compose one actor's behavior from multiple smaller functions.
extend that, either through inheritance or delegation, is to use This is is possible because an actor's :meth:`receive` method returns an ``Actor.Receive``, which is a type alias for ``PartialFunction[Any,Unit]``,
``PartialFunction.orElse`` chaining. 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 .. 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 Initialization patterns
======================= =======================

View file

@ -13,13 +13,12 @@ import akka.event.Logging
//#imports1 //#imports1
import scala.concurrent.Future 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.{ BeforeAndAfterAll, WordSpec }
import org.scalatest.Matchers import org.scalatest.Matchers
import akka.testkit._ import akka.testkit._
import akka.util._ import akka.util._
import scala.concurrent.duration._ import scala.concurrent.duration._
import akka.actor.Actor.Receive
import scala.concurrent.Await import scala.concurrent.Await
//#my-actor //#my-actor
@ -197,25 +196,47 @@ object SwapperApp extends App {
//#receive-orElse //#receive-orElse
abstract class GenericActor extends Actor { trait ProducerBehavior {
// to be defined in subclassing actor this: Actor =>
def specificMessageHandler: Receive
// generic message handler val producerBehavior: Receive = {
def genericMessageHandler: Receive = { case GiveMeThings =>
case event => printf("generic: %s\n", event) sender() ! Give("thing")
}
def receive = specificMessageHandler orElse genericMessageHandler
}
class SpecificActor extends GenericActor {
def specificMessageHandler = {
case event: MyMsg => printf("specific: %s\n", event.subject)
} }
} }
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 //#receive-orElse
class ActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) { class ActorDocSpec extends AkkaSpec(Map("akka.loglevel" -> "INFO")) {