Il s'agit d'une question de conception concernant la mise en œuvre d'un pipeline. Ce qui suit est ma mise en œuvre naïve.
Interface pour les étapes individuelles/les étapes dans le pipeline:
public interface Step<T, U> {
public U execute(T input);
}
Implémentations concrètes des étapes/étapes en cours:
public class StepOne implements Step<Integer, Integer> {
@Override
public Integer execute(Integer input) {
return input + 100;
}
}
public class StepTwo implements Step<Integer, Integer> {
@Override
public Integer execute(Integer input) {
return input + 500;
}
}
public class StepThree implements Step<Integer, String> {
@Override
public String execute(Integer input) {
return "The final amount is " + input;
}
}
La classe de pipeline conservera/enregistrera les étapes dans le pipeline et les exécutera l'une après l'autre:
public class Pipeline {
private List<Step> pipelineSteps = new ArrayList<>();
private Object firstStepInput = 100;
public void addStep(Step step) {
pipelineSteps.add(step);
}
public void execute() {
for (Step step : pipelineSteps) {
Object out = step.execute(firstStepInput);
firstStepInput = out;
}
}
}
Programme de plongée pour exécuter le pipeline:
public class Main {
public static void main(String[] args) {
Pipeline pipeline = new Pipeline();
pipeline.addStep(new StepOne());
pipeline.addStep(new StepTwo());
pipeline.addStep(new StepThree());
pipeline.execute();
}
}
Cependant, comme vous pouvez le constater, la mise en œuvre naïve présente de nombreuses limites.
L’un des principaux est que, puisque l’exigence de chaque étape peut être de n'importe quel type, l’implémentation naïve n’est pas sécurisée contre le type (la méthode execute de la classe Pipeline). S'il m'est arrivé de câbler les étapes dans le pipeline de manière incorrecte, l'application échouera.
Quelqu'un peut-il m'aider à concevoir la solution en ajoutant à ce que j'ai codé ou à me diriger vers un modèle existant pour résoudre ce problème?
Je me concentrerais sur
S'il m'est arrivé de câbler les étapes dans le pipeline de manière incorrecte, l'application échouera.
Oui, c'est un problème. StepThree
est l'étranger ici. Je ne pense pas qu'un modèle simple puisse aider, je pense que ce doit être une combinaison de stratégie et de modèle constructeur. Par exemple:
Pipeline<Integer,Integer> intPipe = new Pipeline<>();
intPipe = intPipe.add(new StepOne()); // increment 100
intPipe = intPipe.add(new StepTwo()); // increment 500
Pipeline<String, Integer> strPipe = intPipe.add(new StepThree()); // convert
Whereat Pipeline est comme ceci:
public static class Pipeline<IN, OUT> {
//...
public<A> Pipeline<OUT,A> add(Step<IN,A> step) {
pipelineSteps.add(step);
return (Pipeline<OUT,A>)this;
}
}
En utilisant la syntaxe fast-builder, ceci pourrait fonctionner:
Pipeline<String, Integer> pipe = new Pipeline<Integer, Integer>()
.add(new StepOne()).add(new StepTwo()).add(new StepThree());
Cela devrait fonctionner puisque les génériques ne font pas partie du bytecode.
pourquoi avez-vous eu besoin d'une classe supplémentaire Pipeline
? Je pense que vous pouvez supprimer l'homme du milieu. cela simplifie votre api, par exemple:
Step<Integer, String> source = Step.of(Object::toString);
Step<Integer, Integer> toHex = source.pipe(it -> Integer.parseInt(it, 16));
toHex.execute(11/*0x11*/);// return 17;
vous pouvez implémenter votre modèle de pipeline simplement dans Java-8 comme ci-dessous:
interface Step<I, O> {
O execute(I value);
default <R> Step<I, R> pipe(Step<O, R> source) {
return value -> source.execute(execute(value));
}
static <I, O> Step<I, O> of(Step<I, O> source) {
return source;
}
}
dans les versions antérieures de Java, vous pouvez utiliser une classe abstraite à la place:
abstract static class Step<I, O> {
public abstract O execute(I value);
public <R> Step<I, R> pipe(Step<O, R> source) {
return new Step<I, R>() {
@Override
public R execute(I value) {
return source.execute(Step.this.execute(value));
}
};
}
public static <I, O> Step<I, O> of(Step<I, O> source) {
return source;
}
}
Votre approche est très bonne. Cependant, je coderais la classe Pipeline comme ceci:
public class Pipeline {
private List<Step> pipelineSteps = new ArrayList<>();
private Object firstStepInput = 100;
public Pipeline() {
pipelineSteps.add(new StepOne());
pipelineSteps.add(new StepTwo());
pipelineSteps.add(new StepThree());
}
public void execute() {
for (Step step : pipelineSteps) {
Object out = step.execute(firstStepInput);
firstStepInput = out;
}
}
public String getResult() {
return (String) firstStepInput;
}
}
De cette manière, toute la connaissance des étapes spécifiques est encapsulée dans la classe Pipeline.
Dans ce cas, la méthode execute peut effectuer une boucle. Cependant, la classe execute peut exécuter les étapes une par une, si nécessaire.
public class Pipeline {
private List<Step> pipelineSteps = new ArrayList<>();
private Object firstStepInput = 100;
public Pipeline() {
pipelineSteps.add(new StepOne());
pipelineSteps.add(new StepTwo());
pipelineSteps.add(new StepThree());
}
Vous pouvez utiliser fondamentalement le modèle de conception de la chaîne de responsabilité