Je travaille sur la refactorisation de certains aspects d'un service Web existant. La façon dont les API de service sont implémentées est d'avoir une sorte de "pipeline de traitement", où il y a des tâches qui sont exécutées en séquence. Sans surprise, les tâches ultérieures peuvent nécessiter des informations calculées par des tâches antérieures, et actuellement la manière de procéder consiste à ajouter des champs à une classe "état du pipeline".
J'ai pensé (et j'espère?) Qu'il y a une meilleure façon de partager des informations entre les étapes du pipeline que d'avoir un objet de données avec des champs de zillion, dont certains ont du sens pour certaines étapes de traitement et pas pour d'autres. Ce serait une douleur majeure de rendre cette classe thread-safe (je ne sais pas si ce serait même possible), il n'y a aucun moyen de raisonner sur ses invariants (et il est probable qu'elle n'en ait pas).
Je parcourais le livre sur les modèles de conception de Gang of Four pour trouver de l'inspiration, mais je n'avais pas l'impression qu'il y avait une solution (Memento était un peu dans le même esprit, mais pas tout à fait). J'ai également regardé en ligne, mais la seconde fois que vous recherchez "pipeline" ou "workflow", vous êtes inondé d'informations sur les tuyaux Unix ou de moteurs et frameworks de workflow propriétaires.
Ma question est la suivante: comment aborderiez-vous la question de l'enregistrement de l'état d'exécution d'un pipeline de traitement logiciel, afin que les tâches ultérieures puissent utiliser les informations calculées par les précédentes? Je suppose que la principale différence avec les canaux Unix est que vous ne vous souciez pas seulement de la sortie de la tâche immédiatement précédente.
Comme demandé, un pseudocode pour illustrer mon cas d'utilisation:
L'objet "contexte de pipeline" a un tas de champs que les différentes étapes du pipeline peuvent remplir / lire:
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
}
Chacune des étapes du pipeline est également un objet:
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;
}
}
De même pour une hypothétique FooStep
, qui pourrait avoir besoin de la barre calculée par BarStep avant elle, ainsi que d'autres données. Et puis nous avons le véritable appel 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);
}
}