Énoncé du problème:
Je dois traiter une demande similaire à un pipeline.
Par exemple:
Lorsqu'une requête arrive, elle doit subir une séquence d'opérations, comme (step1, step2, step3 ...).
Donc, pour y parvenir, j'utilise Modèle de conception de modèle .
Veuillez vérifier et suggérer si j'implémente correctement ce problème ou s'il existe une meilleure solution.
Je soupçonne que mon approche introduira des odeurs de code, car je change très fréquemment les valeurs des objets.
Aussi, suggérer si je et comment puis-je utiliser Java 8 pour accomplir cela? Merci.
Code:
package com.example.demo.design; import java.util.List; public abstract class Template { @Autowired private Step1 step1; @Autowired private Step2 step2; @Autowired private Save save; List<String> stepOutput = null; List<String> stepOutputTwo = null; List<String> stepOutputThree = null; public void step1(String action1) { stepOutput = step1.method(action1); } public void step2(String action2) { stepOutputTwo = step2.method(stepOutput, action2); } abstract public void step3(); public void save() { save.persist(stepOutputThree); } final public void run(String action1, String action2) { step1(action1); step2(action2); stepOutputTwo = step3(); } }
3 Réponses :
J'ai eu le même problème! vous pouvez faire quelque chose comme ceci: et la méthode uncheckCall
sert à gérer les exceptions.
public static <T> T uncheckCall(Callable<T> callable) { try { return callable.call(); } catch (RuntimeException e) { // throw BusinessException.wrap(e); } catch (Exception e) { //throw BusinessException.wrap(e); } }
pour uncheckCall méthode:
final public void run(String action1, String action2) { //other stuffs Stream.of(step1.method(action1)) .map(stepOutput->uncheckCall(() ->step2.method(stepOutput,action2))) .forEach(stepOutputThree -> uncheckCall(()->save.persist(stepOutputThree))); //..... }
Merci d'avoir répondu. Mais je ne veux rien renvoyer du flux. Ma classe Template sera implémentée par les classes concrètes et elles remplaceront simplement l'étape 3 ().
Je viens d'écrire un semi-code pour représenter une feuille de route. Cependant, je pense que vous pouvez modifier votre signature de méthode qui renvoie quelque chose au lieu de déclarer les données membres et de les initier.
Dans le modèle de flux Java 8, cela pourrait ressembler à ceci:
final public void run(String action1, String action2) { Stream.of(action1) // Stream<String> .map(s -> step1.method(s)) // Stream<List<String>> .map(l -> step2.method(l,action2) // Stream<List<String>> .map(l -> step3.method(l)) // Stream<List<String>> .forEach(l -> save.persist(l)); }
Eh bien, quand il y a des "pipelines", "séquence d'opérations", etc., le premier modèle de conception qui me vient à l'esprit est Chain of Responsibility, qui ressemble à ce qui suit
et vous offre les avantages suivants:
Un exemple d'utilisation du monde réel est Filtres de servlet où vous appelez doFilter (HttpRequest, HttpResponse, FilterChain)
pour appeler le gestionnaire suivant
public class StepsInvoker { // spring will put all the steps into this collection in order they were declared // within the spring context (or by means of `@Order` annotation) @Autowired private List<Step> steps; public void invoke(String action1, String action2) { StepContext ctx = new StepContext(); ctx.setAttribute("action1", action1); ctx.setAttribute("action2", action2); steps.forEach(step -> step.handle(ctx)) } }
En cas d'utilisation modèle de chaîne de responsabilité classique, votre pipeline de traitement peut ressembler à ceci:
public class Step1 implements Step { public void handle(StepContext ctx) { String action1 = ctx.getAttribute("action1"); List<String> output1 = doSomething(action1); ctx.setAttribute("output1", output1); } } public class Step2 implements Step { public void handle(StepContext ctx) { String action2 = ctx.getAttribute("action2"); List<String> output1 = ctx.getAttribute("output1"); List<String> output2 = doSomething(output1, action2); ctx.setAttribute("output2", output2); } } public class Step3 implements Step { public void handle(StepContext ctx) { String action2 = ctx.getAttribute("action2"); List<String> output2 = ctx.getAttribute("output2"); persist(output2); } }
public class StepContext { private Map<String, Object> attributes = new HashMap<>(); public <T> T getAttribute(String name) { (T) attributes.get(name); } public void setAttribute(String name, Object value) { attributes.put(name, value); } } public interface Step { void handle(StepContext ctx); }
Step step3 = new Step3(null); Step step2 = new Step2(step3); Step step1 = new Step1(step2); StepContext ctx = new StepContext(); ctx.setAttribute("action1", action1); ctx.setAttribute("action2", action2); step1.handle(ctx);
De plus, tout cela peut être simplifié en une chaîne de gestionnaires découplés les uns des autres en supprimant le next
correspondant références au cas où votre pipeline de traitement devra toujours invoquer toutes les étapes disponibles sans contrôler la nécessité de l'invocation depuis la précédente:
public class Step1 extends AbstractStep { public Step1(Step next) { super(next); } public void handle(StepContext ctx) { String action1 = ctx.getAttribute("action1"); List<String> output1 = doSomething(action1); ctx.setAttribute("output1", output1); next(ctx); // invoke next step } } public class Step2 extends AbstractStep { public Step2(Step next) { super(next); } public void handle(StepContext ctx) { String action2 = ctx.getAttribute("action2"); List<String> output1 = ctx.getAttribute("output1"); List<String> output2 = doSomething(output1, action2); ctx.setAttribute("output2", output2); next(ctx); // invoke next step } } public class Step3 extends AbstractStep { public Step3(Step next) { super(next); } public void handle(StepContext ctx) { String action2 = ctx.getAttribute("action2"); List<String> output2 = ctx.getAttribute("output2"); persist(output2); next(ctx); // invoke next step } }
public class StepContext { private Map<String, Object> attributes = new HashMap<>(); public <T> T getAttribute(String name) { (T) attributes.get(name); } public void setAttribute(String name, Object value) { attributes.put(name, value); } } public interface Step { void handle(StepContext ctx); } public abstract class AbstractStep implements Step { private Step next; public AbstractStep() { } public AbstractStep(Step next) { this.next = next; } protected void next(StepContext ctx) { if (next != null) { next.handle(ctx); } } }
Notez que dans le cas du framework Spring (juste remarqué @Autowired
ann otation) le code client peut être encore plus simplifié car l'annotation @Autowired
peut être utilisée pour injecter tous les beans du type correspondant dans une collection correspondante.
Voici ce que le documentation a> déclare:
Autowiring Arrays, Collections, and Maps
Dans le cas d'un type de dépendance tableau, collection ou mappage, le conteneur transmet automatiquement tous les beans correspondant au type de valeur déclaré. À ces fins, les clés de mappage doivent être déclarées en tant que type String qui seront résolues en noms de bean correspondants. Une telle collection fournie par le conteneur sera ordonnée, en tenant compte des valeurs Ordered et @Order des composants cibles, sinon en suivant leur ordre d'enregistrement dans le conteneur. Alternativement, un seul bean cible correspondant peut également être une collection généralement typée ou une carte elle-même, injectée comme telle.
protected void doFilter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) { if (haveToInvokeNextHandler) { chain.doFilter(req, resp); } }
Veuillez consulter les liens suivants stackoverflow.com/questions/39947155/ … stackoverflow.com/questions/39947155/… a>
Cela me semble être une bonne question pour codereview.stackexchange.com .
Oui que le TMP, une dernière séquence d'opérations et au moins une opération abstraite à implémenter ultérieurement.