Ich arbeite daran, bestimmte Aspekte eines vorhandenen Webdienstes neu zu faktorisieren. Die Implementierung der Service-APIs erfolgt über eine Art "Verarbeitungs-Pipeline", in der Aufgaben nacheinander ausgeführt werden. Es ist nicht überraschend, dass für spätere Tasks möglicherweise Informationen erforderlich sind, die von früheren Tasks berechnet wurden. Derzeit erfolgt dies durch Hinzufügen von Feldern zu einer "Pipeline-Status" -Klasse.
Ich habe gedacht (und gehofft?), Dass es einen besseren Weg gibt, Informationen zwischen Pipelineschritten auszutauschen, als ein Datenobjekt mit zig Feldern zu haben, von denen einige für einige Verarbeitungsschritte und andere nicht sinnvoll sind. Es wäre ein großer Schmerz, diese Klasse threadsicher zu machen (ich weiß nicht, ob es überhaupt möglich wäre), es gibt keine Möglichkeit, über ihre Invarianten zu argumentieren (und es ist wahrscheinlich, dass es keine gibt).
Ich habe das Buch mit den Gang of Four-Designmustern durchgeblättert, um Inspiration zu finden, aber ich hatte nicht das Gefühl, dass darin eine Lösung steckt (Memento war einigermaßen im selben Sinne, aber nicht ganz). Ich habe auch online gesucht, aber sobald Sie nach "Pipeline" oder "Workflow" suchen, werden Sie entweder mit Unix-Pipes-Informationen oder proprietären Workflow-Engines und Frameworks überflutet.
Meine Frage lautet: Wie würden Sie das Problem der Aufzeichnung des Ausführungsstatus einer Software-Verarbeitungs-Pipeline angehen, damit spätere Aufgaben die von früheren berechneten Informationen verwenden können? Ich denke, der Hauptunterschied zu Unix-Pipes ist, dass Sie sich nicht nur um die Ausgabe der unmittelbar vorhergehenden Aufgabe kümmern.
Wie gewünscht, ein Pseudocode zur Veranschaulichung meines Anwendungsfalls:
Das Objekt "Pipeline-Kontext" verfügt über eine Reihe von Feldern, die die verschiedenen Pipeline-Schritte füllen / lesen können:
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
}
Jeder der Pipelineschritte ist auch ein Objekt:
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;
}
}
Ähnliches gilt für eine FooStep
Hypothese, für die der von BarStep berechnete Balken zusammen mit anderen Daten erforderlich sein könnte. Und dann haben wir den wirklichen API-Aufruf:
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);
}
}