1
votes

Suggestion de modèle de conception pour effectuer une opération de pipeline

É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 commentaires

Veuillez consulter les liens suivants stackoverflow.com/questions/39947155/ … stackoverflow.com/questions/39947155/…


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.


3 Réponses :


1
votes

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)));

    //.....

}


2 commentaires

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.



1
votes

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));
}


0 commentaires

1
votes

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

 Chaîne de responsabilité a>

et vous offre les avantages suivants:

  1. vous permet d'ajouter de nouveaux gestionnaires si nécessaire (par exemple au moment de l'exécution) sans modifier les autres gestionnaires et la logique de traitement (principe ouvert / fermé de SOLID)
  2. permet à un gestionnaire d'arrêter le traitement d'une requête si nécessaire
  3. vous permet de découpler la logique de traitement des gestionnaires les uns des autres (principe de responsabilité unique de SOLID)
  4. vous permet de définir l'ordre des gestionnaires pour traiter une requête en dehors des gestionnaires eux-mêmes

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:

API

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);
    }
}

Implémentation

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);
}

Code client

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:

API

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
    }
}

Implémentation

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);
        }
    }
}

Code client

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);
    }
}


0 commentaires