Ho due attori Akka che rispondono ad alcuni messaggi allo stesso modo, ma altri in modo diverso. Entrambi rispondono allo stesso insieme di messaggi. Ti chiedi come progettare i miei due attori con i loro metodi di ricezione, via ereditarietà, compostezza, ecc.? Ho provato a concatenare le funzioni parziali di altri tratti con "orElse", che sfortunatamente espone la classe alla funzionalità del suo tratto, inoltre non ero sicuro di come la ricezione del tratto potesse facilmente accedere al contesto dell'attore. Una soluzione modulare e integrata potrebbe essere l'ideale, ma mi chiedo se si tratta di un problema risolto da qualche parte?Come condivido il comportamento tra gli attori di Akka?
risposta
C'è davvero un sacco di modi per procedere. Ne elencherò due dal modo OO (simile a ciò che @Randal Schulz suggerisce) e 1 altro modo funzionale. Per la prima soluzione possibile, si potrebbe fare qualcosa di semplice come questo:
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 questo approccio, si definisce sostanzialmente un abstract impl attore cui è definita la funzione di receive
per tutti i messaggi che vengono gestiti. I messaggi sono delegati a def
s dove la logica di business reale sarebbe. Due sono astratte, lasciando che le classi concrete definiscano la gestione e una è pienamente implementata per un caso in cui la logica non ha bisogno di differire.
Ora una variante di questo approccio utilizzando il modello di strategia:
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)
}
}
Qui l'approccio è quello di passare una classe strategia durante la costruzione che definisce la manipolazione di A e C, con b nuovamente manipolato stesso indipendentemente. I due approcci sono abbastanza simili e raggiungono lo stesso obiettivo. L'ultimo approccio utilizza parziale funzione di concatenamento e potrebbe essere la seguente:
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
}
Qui, alcuni tratti sono di impostazione che definiscono i comportamenti gestione dei messaggi per i tipi di messaggio 3. Gli attori concreti si mescolano in questi tratti e poi scelgono quali comportamenti vogliono quando costruiscono la loro funzione receive
usando il concatenamento di funzioni parziali.
Probabilmente ci sono molti altri modi per fare ciò che cerchi, ma ho pensato di buttare fuori alcune opzioni per te. Spero che sia d'aiuto.
Grazie! Ma non sono sicuro se questo allevia uno dei due problemi che stavo combattendo. La 3a via oElse sembra avere lo stesso problema di quello che l'esempio di attore di Scala ha avuto .. accoppiando strettamente la classe al metodo handle all'interno del tratto. Gli altri 2 sono simili all'esempio "receiveWrapper", in cui le chiamate ricevono qualcosa che è possibile ignorare. Lo schema ereditarietà/strategia funzionerebbe. L'accesso al contesto è risolto con un parametro ActorContext per il metodo wrapper.Mi sono reso conto che nel mio caso gli unici messaggi gestiti nello stesso modo sono quelli sconosciuti, quindi probabilmente non ne vale la pena. – sdanzig
Non so se ero solo stanco, ma sì, sicuramente non è un grande mistero per questo, con le soluzioni che hai proposto con un parametro ActorContext. Tuttavia, non mi pento di averlo chiesto perché sono sicuro che la presentazione di questo tipo aiuterà gli altri :) – sdanzig
Finora non ho avuto motivo di rimpiangere di spingere la maggior parte delle funzionalità effettive dei miei servizi (la cosiddetta "logica aziendale") in un livello inferiore, una libreria "convenzionale" e sincrona (ea volte bloccante) che può essere testato unitariamente senza la complicazione degli attori. L'unica cosa che inserisco nelle classi Actor è lo stato mutabile a lungo termine condiviso su cui agisce il codice di libreria convenzionale. Questo, ovviamente, e la logica di decodifica e invio dei messaggi della funzione Akka Actor receive
.
Se si esegue questa operazione, condividere la logica nel modo in cui si cerca è banale.
I due post più rilevanti che ho trovato includono questo, che mostra il concatenamento delle funzioni parziali, almeno per gli attori di Scala: http://www.kotancode.com/2011/07/19/traits-multiple-inheritance -and-actors-in-scala/... e poi questo, che mostra un modello che consente di ricevere impilabili chiamando un wrapper: http://grokbase.com/t/gg/akka-user/12ckfs8r7g/stackable-actor -trazioni ... Penso che entrambi abbiano i loro punti di forza, ma non so come avrebbero accesso al contesto. – sdanzig