Macarse
Macarse

Reputation: 93163

Where to define case classes when using Actors

I am giving my first steps with scala.

I have created an PhotosLoaderActor that will take care of downloading an image and saving it into a cache. To do this, I will have a CacheActor and a DownloadActor.

My PhotosLoaderActor has this:

override def act() {
  loop {
    react {
      case (caller : Actor, photoToLoad:String) => { // bla bla }

I just learned I could use case classes to use something like this:

case class LoadImage(caller: Actor, photoToLoad: String)
override def act() {
  loop {
    react {
      case LoadImage(caller, photoToLoad) => { // bla bla }

My question is:

Where should I define the case classes? If I am calling the PhotosLoaderActor from a different package, importing the actor would also import the case classes? Which is the best practice?

Upvotes: 3

Views: 2931

Answers (3)

mpilquist
mpilquist

Reputation: 3855

I've tried a few different approaches and settled on putting all related messages in to an object, typically named something like XyzProtocol.

For example:

object PhotoProtocol {
  case class LoadImage(caller: Actor, photoToLoad: String)
  case class UpdateCache(photoToLoad: String, photo: Photo)
  case object ClearCache
}

I like this approach for a few reasons:

  1. Scanning the containing package, either in an IDE or in ScalaDoc, shows only 1 or a few XyzProtocol objects instead of being littered with dozens of messages.

  2. If the number of actors required to process a message changes over time (e.g., introduction of an actor pool or indirection to another actor subtree), the messages are not impacted.

  3. There's no accidental capture of the actor instance, as is there when defining the case classes inside the actor class. This accidental capture is reason enough for me to never define the case classes inside the actor class.

Note: if the message is purely an internal implementation detail of an actor (e.g., an internal signal sent to itself by itself), I define the message in the companion object of the actor class.

PS. I also suggest moving to Akka from standard library actors. Akka will be added to Scala 2.10 distribution.

Upvotes: 11

jwinder
jwinder

Reputation: 378

I often define them above the actor that uses them. Or in file dedicated to just those case classes if multiple actors will match on the same cases.

package org.numbers
import akka.actor.Actor

sealed trait Numbers
case class Even(n: Int) extends Numbers
case class Odd(n: Int) extends Numbers

class NumberActor extends Actor {
  def receive = {
    case Even(n) => doEven(n)
    case Odd(n) => doOdd(n)
  }

  def doEven = ...
  def doOdd = ...
}

Uses akka instead of scala actors, but either way. This way, importing the package containing the actor will also import the messages that can be sent to that actor. If the case classes are defined "inside" the actor, then they won't be available to other classes that want to send messages to that actor.

Upvotes: 1

James
James

Reputation: 3362

If the Actor is a singleton object then I'd probably put it inside the object itself.

Upvotes: 1

Related Questions