In questo articolo, esploreremo i diversi modi di implementare il pipeline design pattern partendo dalle basi fino a soluzioni più complesse.
Il Pipeline Pattern
Il pipeline pattern è un pattern di progettazione software che fornisce la possibilità di costruire ed eseguire una sequenza di operazioni.
Questo pattern è meglio usato insieme al plugin pattern, per costruire dinamicamente la pipeline quando l’applicazione si avvia.
Sequenza
L’implementazione più basilare di una pipeline sarebbe una semplice sequenza di operazioni.
L’interfaccia di un’operazione può essere invocata per elaborare i dati.
La pipeline processa ogni operazione una per una. La classe pipeline implementa anche l’interfaccia IOperation in modo che possano essere combinate.
L’operazione può essere scritta in una classe dedicata.
Oppure usa un wrapper per creare automaticamente un’operazione da una lambda.
Le operazioni della pipeline devono essere registrate prima che la pipeline sia invocata.
Interruttore di circuito
La prima caratteristica che vuoi aggiungere alla tua pipeline è aggiungere un interruttore di circuito.
Ogni operazione restituirà il risultato: fallimento o successo.
Se un’operazione fallisce, l’esecuzione della pipeline dovrebbe fermarsi.
Asincrono
Un altro requisito potrebbe essere quello di avere una pipeline che possa gestire operazioni asincrone.
Ora ogni operazione dovrà chiamare l’operazione successiva nella pipeline, dopo aver finito di elaborare i dati.
La pipeline è un po’ più complessa, perché deve impostare l’operazione successiva quando viene registrata una nuova operazione. Un’altra soluzione è usare un costruttore.
Questa operazione è asincrona e verrà eseguita in un thread dedicato, quando il tempo è finito, invocherà l’operazione successiva per continuare la pipeline.
L’operazione generica può essere usata sia con una semplice azione, o usare un interruttore integrato se si usa una funzione.
Questo semplice esempio utilizza diverse delle funzioni che abbiamo implementato.
Ora sai come rendere asincrona la tua pipeline!
Sarebbe ancora meglio se avesse un altro callback all’operazione precedente in modo da avere il risultato che va in controcorrente attraverso la pipeline.
Plugin
La ragione principale per usare lo schema di progettazione della pipeline è spesso l’esigenza di poter aggiungere plugin che aggiungano operazioni a una pipeline esistente o che aggancino un’operazione nel mezzo di essa.
La pipeline è davvero basilare, ma questa volta le operazioni sono esposte.
Prendiamo una semplice applicazione con una pipeline che mostrerà solo i 3 passi nella console. Questa applicazione supporta anche dei plugin per modificare la pipeline.
Il primo plugin aggancia un’altra operazione nel secondo slot della pipeline.
Il secondo plugin aggiunge una nuova operazione alla fine della pipeline.
L’applicazione e i plugin sono messi insieme, possiamo invocare la pipeline.
La pipeline invoca l’operazione successiva solo per gli elementi che non sono falliti.
High Performance Pipeline
Il Pipeline Design Pattern può anche essere riferito a un’architettura software molto più specifica e orientata alle prestazioni.
Alcuni progetti usano una pipeline per ottimizzare l’elaborazione di una grande quantità di dati eseguendo ogni operazione della pipeline in un thread dedicato.
Ogni thread consumerà e produrrà dati da una coda concorrente che fungerà da buffer.
Questa volta, useremo operazioni asincrone con un interruttore.
Se l’operazione ha successo, dovrebbe invocare il prossimo, o terminare se fallisce.
La pipeline è progettata per bypassare il circuit-breaker. Successo o fallimento, continuerà sempre la sequenza della pipeline madre e chiamerà l’operazione successiva.
Lo scenario è un po’ complesso, quindi non spiegherò tutto. L’idea è di avere diversi thread per elaborare gli ordini in arrivo. Quando un ordine ha finito di essere processato, controlliamo lo stato dell’ordine.
Ogni processore d’ordine è isolato in un thread dedicato, in modo da poter ottimizzare il modo in cui si memorizzano i dati e avere un accesso diretto alla memoria senza usare lock.
Il processore dell’ordine di pagamento è l’unico thread che può accedere ai saldi utente. Può ottenere o aggiornare qualsiasi saldo senza preoccuparsi dei problemi di concorrenza.
La pipeline sta elaborando la sequenza di operazioni il più velocemente possibile.
Conclusione
Il Pipeline Design Pattern ha un sacco di modi molto diversi di essere implementato, da una semplice Chain of Command a un Workflow più complesso.
Molte caratteristiche possono essere richieste come circuit-breaker, asincrono o buffer, ma la maggior parte delle volte, si vogliono usare le pipeline quando si vogliono avere plugin che possono agganciarsi al flusso di esecuzione della pipeline.