diff --git a/akka-docs/rst/scala/actors.rst b/akka-docs/rst/scala/actors.rst index 7d70b4cfb0..4e7be27c2f 100644 --- a/akka-docs/rst/scala/actors.rst +++ b/akka-docs/rst/scala/actors.rst @@ -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 ======================= diff --git a/akka-docs/rst/scala/code/docs/actor/ActorDocSpec.scala b/akka-docs/rst/scala/code/docs/actor/ActorDocSpec.scala index 0de510aab6..14a98cab76 100644 --- a/akka-docs/rst/scala/code/docs/actor/ActorDocSpec.scala +++ b/akka-docs/rst/scala/code/docs/actor/ActorDocSpec.scala @@ -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")) {