Sto lavorando per ridisegnare alcuni aspetti di un servizio web esistente. Il modo in cui le API dei servizi vengono implementate consiste nel disporre di una sorta di "pipeline di elaborazione", in cui sono presenti attività eseguite in sequenza. Non sorprendentemente, le attività successive potrebbero richiedere informazioni calcolate da attività precedenti, e attualmente il modo in cui viene fatto è aggiungere campi a una classe "stato pipeline".
Ho pensato (e spero?) che ci sia un modo migliore per condividere le informazioni tra i passaggi della pipeline piuttosto che avere un oggetto dati con un campo di zillion, alcuni dei quali hanno senso per alcuni passaggi di elaborazione e non per altri. Sarebbe un grosso problema rendere questa classe sicura per i thread (non so se sarebbe possibile), non c'è modo di ragionare sui suoi invarianti (ed è probabile che non ne abbia).
Stavo sfogliando il libro dei modelli di Gang of Four per trovare qualche ispirazione, ma non mi sembrava che ci fosse una soluzione lì dentro (Memento era un po 'nello stesso spirito, ma non del tutto). Ho anche guardato in linea, ma nel secondo in cui cerchi "pipeline" o "flusso di lavoro" vieni sommerso dalle informazioni sulle pipe Unix o da motori e framework proprietari del flusso di lavoro.
La mia domanda è: come affronti il problema della registrazione dello stato di esecuzione di una pipeline di elaborazione del software, in modo che le attività successive possano utilizzare le informazioni calcolate da quelle precedenti? Immagino che la principale differenza con i 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 diversi 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 richiedere la barra calcolata da BarStep prima di esso, 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);
}
}