2011-12-30 14 views
20

Considerare questi due tratti:Composizione comportamento tratto a Scala in un Akka ricevono metodo

trait Poked extends Actor { 
    override def receive = { 
    case Poke(port, x) => ReceivePoke(port, x) 
    } 

    def ReceivePoke(port: String, x: Any) 
} 

trait Peeked extends Actor { 
    override def receive = { 
    case Peek(port) => ReceivePeek(port) 
    } 

    def ReceivePeek(port: String) 
} 

Consideriamo ora posso creare un nuovo attore che implementa entrambi i caratteri:

val peekedpoked = actorRef(new Actor extends Poked with Peeked) 

Come faccio a comporre la ricevere gestori? vale a dire, il ricevitore deve essere qualcosa come il seguente codice, anche se "generata automaticamente" (vale a dire, tutti i tratti devono comporre):

def receive = (Poked.receive: Receive) orElse (Peeked.receive: Receive) orElse ... 

risposta

27

È possibile utilizzare super[T] fare riferimento a membri di particolari super-classi/tratti.

Ad esempio:

trait IntActor extends Actor { 
    def receive = { 
     case i: Int => println("Int!") 
    } 
} 

trait StringActor extends Actor { 
    def receive = { 
     case s: String => println("String!") 
    } 
} 

class IntOrString extends Actor with IntActor with StringActor { 
    override def receive = super[IntActor].receive orElse super[StringActor].receive 
} 

val a = actorOf[IntOrString].start 
a ! 5 //prints Int! 
a ! "Hello" //prints String! 

Edit:

In risposta al commento di Hugo, ecco una soluzione che permette di comporre i mixins senza dover cablare manualmente la riceve insieme. Fondamentalmente si tratta di una caratteristica di base con un mutabile List[Receive], e ogni tratto misto in-call chiama un metodo per aggiungere la propria ricezione alla lista.

trait ComposableActor extends Actor { 
    private var receives: List[Receive] = List() 
    protected def registerReceive(receive: Receive) { 
    receives = receive :: receives 
    } 

    def receive = receives reduce {_ orElse _} 
} 

trait IntActor extends ComposableActor { 
    registerReceive { 
    case i: Int => println("Int!") 
    } 
} 

trait StringActor extends ComposableActor { 
    registerReceive { 
    case s: String => println("String!") 
    } 
} 

val a = actorOf(new ComposableActor with IntActor with StringActor).start 
a ! 5 //prints Int! 
a ! "test" //prints String! 

L'unica cosa da tenere a mente è che l'ordine del riceve non deve essere importante, dal momento che non sarà in grado di prevedere facilmente quale è il primo della catena, anche se si potrebbe risolvere che, usando una hashmap mutabile invece di una lista.

+0

Questo è molto interessante, si :-) grazie Ma esso presuppone la preesistenza di un tipo IntOrString che è sia un int e una stringa, e che IntOrString sa che dovrebbe comporre quelli (che, se sto costruendo un quadro, l'altro può ignorare). Non è possibile comporre automaticamente i tratti IntActor e StringActor? –

+3

L'ordine è dato dalla linearizzazione dei tratti misti, quindi "prevedibile" ;-) E usando le corrispondenze in prepending l'override dei tratti successivi wrt. quelli precedenti, quindi penso che la tua soluzione sia molto bella! –

+0

Eccellente display del tuo scala-fu! :-) –

5

È possibile utilizzare la ricezione vuota nella classe attore base e la catena riceve nelle proprie definizioni. campione per Akka 2.0-M2:

import akka.actor.Actor 
import akka.actor.Props 
import akka.event.Logging 
import akka.actor.ActorSystem 

class Logger extends Actor { 
    val log = Logging(context.system, this) 

    override def receive = new Receive { 
    def apply(any: Any) = {} 
    def isDefinedAt(any: Any) = false 
    } 
} 

trait Errors extends Logger { 
    override def receive = super.receive orElse { 
    case "error" => log.info("received error") 
    } 
} 

trait Warns extends Logger { 
    override def receive = super.receive orElse { 
    case "warn" => log.info("received warn") 
    } 
} 

object Main extends App { 
    val system = ActorSystem("mysystem") 
    val actor = system.actorOf(Props(new Logger with Errors with Warns), name = "logger") 
    actor ! "error" 
    actor ! "warn" 
} 
Problemi correlati