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
===============================================
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
=======================

View file

@ -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")) {