Sto lavorando al re-factoring di alcuni aspetti di un servizio web esistente. Il modo in cui vengono implementate le API di servizio è avere una sorta di "pipeline di elaborazione", in cui vi sono attività che vengono eseguite in sequenza. Non sorprende che le attività successive possano richiedere informazioni calcolate dalle attività precedenti e attualmente il modo in cui ciò viene fatto è aggiungendo campi a una classe "stato della pipeline".
Ho pensato (e spero?) Che esista un modo migliore per condividere le informazioni tra i passaggi della pipeline piuttosto che avere un oggetto dati con campi da un milione di miliardi, alcuni dei quali hanno senso per alcuni passaggi di elaborazione e non per altri. Sarebbe una grande sofferenza rendere questa classe thread-safe (non so se sarebbe nemmeno possibile), non c'è modo di ragionare sui suoi invarianti (ed è probabile che non ne abbia).
Stavo sfogliando il libro dei modelli di design di Gang of Four per trovare un po 'di ispirazione, ma non mi sembrava che ci fosse una soluzione (Memento era un po' nello stesso spirito, ma non del tutto). Ho anche cercato online, ma nel momento in cui cerchi "pipeline" o "flusso di lavoro", sei invaso dalle informazioni sui tubi Unix o dai motori e dai framework di flusso di lavoro proprietari.
La mia domanda è: come affronteresti il problema della registrazione dello stato di esecuzione di una pipeline di elaborazione software, in modo che le attività successive possano utilizzare le informazioni calcolate da quelle precedenti? Immagino che la differenza principale con le pipe Unix sia che non ti interessa solo l'output dell'attività immediatamente precedente.
Come richiesto, alcuni pseudocodici per illustrare il mio caso d'uso:
L'oggetto "contesto della pipeline" ha una serie di campi che i vari passaggi della pipeline possono popolare / leggere:
public class PipelineCtx {
... // fields
public Foo getFoo() { return this.foo; }
public void setFoo(Foo aFoo) { this.foo = aFoo; }
public Bar getBar() { return this.bar; }
public void setBar(Bar aBar) { this.bar = aBar; }
... // more methods
}
Ciascuno dei passaggi della pipeline è anche un oggetto:
public abstract class PipelineStep {
public abstract PipelineCtx doWork(PipelineCtx ctx);
}
public class BarStep extends PipelineStep {
@Override
public PipelineCtx doWork(PipelieCtx ctx) {
// do work based on the stuff in ctx
Bar theBar = ...; // compute it
ctx.setBar(theBar);
return ctx;
}
}
Allo stesso modo per un ipotetico FooStep
, che potrebbe aver bisogno della barra calcolata da BarStep prima di essa, insieme ad altri dati. E poi abbiamo la vera chiamata API:
public class BlahOperation extends ProprietaryWebServiceApiBase {
public BlahResponse handle(BlahRequest request) {
PipelineCtx ctx = PipelineCtx.from(request);
// some steps happen here
// ...
BarStep barStep = new BarStep();
barStep.doWork(crx);
// some more steps maybe
// ...
FooStep fooStep = new FooStep();
fooStep.doWork(ctx);
// final steps ...
return BlahResponse.from(ctx);
}
}