Progettazione del componente asincrono

4

Sto provando a progettare un componente asincrono. I requisiti su questo componente sono:

  • Il componente potrebbe ricevere eventi in qualsiasi momento
  • Il componente potrebbe avviare un'operazione di lunga durata e attendere il risultato. Questa operazione viene eseguita dal sistema esterno.
  • L'operazione a lungo termine potrebbe essere annullata, sia dal componente che esternamente
  • Gli eventi potrebbero venire mentre è in esecuzione l'operazione di lunga durata, che potrebbe riavviare l'operazione di lunga durata, eventualmente annullando l'invocazione precedente
  • I componenti devono decidere cosa fare con l'evento che arriva durante l'operazione di lunga durata, se ignorarlo, metterlo in coda o qualcos'altro
  • Il componente potrebbe iniziare diverse operazioni in risposta a diversi eventi
  • Il componente può avere più "valori di supporto", in cui ciascuno ha un valore corrente e la sua richiesta di modifica è un componente evento che deve reagire a
  • Deve essere possibile testare il componente in isolamento (questo è importante!)
  • Ci saranno più componenti diversi, alcuni potrebbero condividere comportamenti. Ad esempio la possibilità di accendere / spegnere il componente.

Finora ho cercato / pensato di:

  • Osservabili reattivi
    • Questo è ciò che viene utilizzato in questo momento
    • Mi piace molto il modo in cui eseguire i test in tempo virtuale
    • Le operazioni di lunga durata sono rappresentate come funzioni che trasformano la "richiesta" osservabile in "risposta" osservabile
    • Ma il resto del team considera questa soluzione troppo complessa
  • Oggetti semplici con metodi per gestori di eventi
    • Complessivamente semplice, ma alcune costruzioni potrebbero essere complesse
    • Non richiede librerie aggiuntive
    • Il test non è semplice, poiché è necessario scrivere esplicitamente ogni risposta all'evento invece di dire "per ogni richiesta, rispondere in 5 tick" come in reattivo
  • Attività con async / await
    • Non so come sarebbe possibile implementare la situazione che si verifica quando l'evento si verifica durante operazioni di lunga durata e come testare questa situazione, motivo per cui scelgo reattivo invece
  • Quadro degli attori (Akka)
    • Questo sembra essere il più adatto per i miei requisiti
    • Ma non mi piace il modo in cui viene effettuato il test, poiché utilizza in tempo reale
    • E credo che Akka sia un modo eccessivo per questo caso d'uso, poiché non abbiamo bisogno del threading per componente e di tutte le strumentazioni per creare attori e comunicare tra loro
    • E temo che anche il mio team lo considererebbe troppo complesso

Quello che sto cercando sono suggerimenti di possibili altre soluzioni, a cui non avrei pensato. O ulteriori vantaggi / svantaggi delle soluzioni suggerite.

    
posta Euphoric 15.03.2017 - 06:28
fonte

2 risposte

1

Penso che osservabile sia il modello corretto.

Promise / Task / Future / Deferred con async / await è più adatto quando si ha un singolo evento (o una catena logica ramificata di eventi) da seguire. Avrai bisogno di molto lavoro aggiuntivo per adattarlo alla tua situazione.

La maggior parte delle implementazioni utilizza un generatore * / IEnumerable / iterator sotto le copertine comunque, con await che diventa yield new Promise / yield return new Task e la codifica di chiamata che viene convertita in un enumeratore di promesse / attività.

Rx / osservabile è più adatto quando gli eventi possono sparare ripetutamente e vuoi trattarli come una raccolta ripetuta nel tempo.

Potresti eseguire il rollover della tua classe per gestirlo (probabilmente usando async / await ) ma dovrai risolvere molti degli stessi problemi che Rx ha già senza risparmiare su alcuna complessità.

    
risposta data 15.03.2017 - 11:48
fonte
0

La tua soluzione potrebbe essere troppo complessa perché sta facendo troppo dentro di sé. Se, ad esempio, si suddivide la gestione di attività a esecuzione prolungata (processo AKA) nel proprio componente, è possibile limitare l'interazione alla gestione di tali attività. Allo stesso modo, il componente di gestione eventi ad hoc potrebbe dover utilizzare il gestore lavoro, ma limitare i test a quegli eventi può renderlo un po 'più facile da gestire. Ho seguito personalmente questo approccio con un paio di progetti.

Il modo in cui gestisci il test è di un paio di interfacce ben scelte. L'implementazione può essere il tuo osservabile reattivo, o un'implementazione personalizzata, a seconda delle tue vere esigenze. L'interfaccia IJob potrebbe essere simile a questa:

public interface IJob
{
    string Id { get; }
    bool IsRunning { get; }
    Task Handle { get; }
    void Cancel();
    // and anything else you need.
}

Puoi anche creare una versione tipizzata per gestire attività che restituiscono un valore.

La linea di fondo è che potresti aver bisogno solo di uno dei componenti di gestione delle attività a lungo termine nel tuo sistema. Puoi testare facilmente tutte le interazioni con questo componente, e puoi testare il componente stesso abbastanza facilmente. Poiché la proprietà Handle espone un Task puoi facilmente avere il codice che sarà semplicemente await il lavoro da completare.

Non ho lavorato con Reactive Observables, ma puoi fornire un handle Task collegato a un risultato semplice abbastanza facilmente usando un TaskCompletionSource . All'interno del lavoro è possibile impostare il risultato o lo stato di errore quando è necessario esplicitamente, e il codice esterno non è il più saggio. Il TaskCompletsionSource ha una proprietà Task che può essere utilizzata per Handle sopra.

Puoi avere tanti componenti "ad hoc" di cui hai bisogno e, a patto che abbiano un modo coerente di gestire i lavori lunghi, dovrebbe semplificare la progettazione in qualcosa che tutti possano capire.

    
risposta data 13.06.2017 - 15:02
fonte

Leggi altre domande sui tag