actors scala akka actor traits

scala - actors - ¿Cómo puedo compartir mejor el comportamiento entre los actores Akka?



akka actor watch (2)

Hasta ahora no he tenido motivos para lamentarme de haber introducido tanta funcionalidad real de mis servicios (la llamada "lógica empresarial") en una biblioteca de capa inferior, "convencional" y sincrónica (y en ocasiones de bloqueo) que se puede probar por unidades Sin la complicación de los actores. Lo único que coloco en las clases de Actor es el estado mutable a largo plazo compartido en el que actúa ese código de biblioteca convencional. Eso, por supuesto, y la lógica de decodificación y despacho de mensajes de la función de receive Akka Actor.

Si haces esto, compartir la lógica en la forma que buscas es trivial.

Tengo dos actores Akka que responden a algunos mensajes de la misma manera, pero a otros de una manera diferente. Ambos responden al mismo conjunto de mensajes. ¿Se pregunta cómo diseñar mis dos actores con sus métodos de recepción, a través de la herencia, la compostura, etc.? Intenté encadenar funciones parciales de otros rasgos con "orElse", que desafortunadamente expone a la clase a la funcionalidad de su rasgo, además no estaba seguro de cómo la recepción del rasgo podría acceder fácilmente al contexto del actor. Una solución modular y de instalación inmediata sería ideal, pero me pregunto si este es un problema resuelto en algún lugar.


Realmente hay un montón de maneras en que puedes hacer esto. Enumeraré dos de la manera OO (similar a lo que sugiere @Randal Schulz) y 1 forma más funcional. Para la primera solución posible, podrías hacer algo simple como esto:

case class MessageA(s:String) case class MessageB(i:Int) case class MessageC(d:Double) trait MyActor extends Actor{ def receive = { case a:MessageA => handleMessageA(a) case b:MessageB => handleMessageB(b) case c:MessageC => handleMessageC(c) } def handleMessageA(a:MessageA) def handleMessageB(b:MessageB) = { //do handling here } def handleMessageC(c:MessageC) } class MyActor1 extends MyActor{ def handleMessageA(a:MessageA) = {} def handleMessageC(c:MessageC) = {} } class MyActor2 extends MyActor{ def handleMessageA(a:MessageA) = {} def handleMessageC(c:MessageC) = {} }

Con este enfoque, usted define básicamente un implemento de actor abstracto donde se define la función de receive para todos los mensajes que se manejan. Los mensajes se delegan a las def donde se ubicaría la lógica empresarial real. Dos son abstractos, permiten que las clases concretas definan el manejo y uno se implementa completamente para un caso en el que la lógica no necesita diferir.

Ahora una variante de este enfoque utilizando el patrón de estrategia:

trait MessageHandlingStrategy{ def handleMessageA(a:MessageA) def handleMessageB(b:MessageB) = { //do handling here } def handleMessageC(c:MessageC) } class Strategy1 extends MessageHandlingStrategy{ def handleMessageA(a:MessageA) = {} def handleMessageC(c:MessageC) = {} } class Strategy2 extends MessageHandlingStrategy{ def handleMessageA(a:MessageA) = {} def handleMessageC(c:MessageC) = {} } class MyActor(strategy:MessageHandlingStrategy) extends Actor{ def receive = { case a:MessageA => strategy.handleMessageA(a) case b:MessageB => strategy.handleMessageB(b) case c:MessageC => strategy.handleMessageC(c) } }

Aquí, el enfoque es pasar una clase de estrategia durante la construcción que define el manejo para a y c, y b se maneja de la misma manera independientemente. Los dos enfoques son bastante similares y logran el mismo objetivo. El último enfoque utiliza el encadenamiento de funciones parciales y podría verse así:

trait MessageAHandling{ self: Actor => def handleA1:Receive = { case a:MessageA => //handle way 1 } def handleA2:Receive = { case a:MessageA => //handle way 2 } } trait MessageBHandling{ self: Actor => def handleB:Receive = { case b:MessageB => //handle b } } trait MessageCHandling{ self: Actor => def handleC1:Receive = { case c:MessageC => //handle way 1 } def handleC2:Receive = { case c:MessageC => //handle way 2 } } class MyActor1 extends Actor with MessageAHandling with MessageBHandling with MessageCHandling{ def receive = handleA1 orElse handleB orElse handleC1 } class MyActor2 extends Actor with MessageAHandling with MessageBHandling with MessageCHandling{ def receive = handleA2 orElse handleB orElse handleC2 }

Aquí, se configuran algunos rasgos que definen los comportamientos de manejo de mensajes para los 3 tipos de mensajes. Los actores concretos se mezclan en esos rasgos y luego seleccionan qué comportamientos desean cuando desarrollan su función de receive mediante el encadenamiento de funciones parciales.

Probablemente hay muchas otras formas de hacer lo que buscas, pero pensé que te iba a ofrecer algunas opciones. Espero eso ayude.