【问题标题】:Design pattern suggestion to perform pipeline operation执行流水线操作的设计模式建议
【发布时间】:2019-12-02 04:38:23
【问题描述】:

问题陈述: 我必须处理类似于管道的请求。
例如:
当一个请求到来时,它必须经过一系列的操作,比如(step1,step2,step3...)。
因此,为了实现这一点,我使用 模板设计模式

请查看并建议我是否正确解决了这个问题,或者有更好的解决方案。
我怀疑我的方法会引入代码异味,因为我经常更改对象的值。

另外,建议我是否可以使用 Java 8 来完成此任务? 谢谢。

代码:

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

【问题讨论】:

标签: java design-patterns java-8


【解决方案1】:

在 Java 8 流模型中,可能如下所示:

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

【讨论】:

    【解决方案2】:

    我有同样的问题!你可以这样做:uncheckCall 方法用于处理异常。

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

    对于uncheckCall 方法:

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

    【讨论】:

    • 感谢您的回答。但我不想从流中返回任何东西。我的模板类将由具体类实现,它们只会覆盖 step3()。
    • 我刚刚写了一个半代码来表示一个路线图。但是我认为您可以更改返回某些内容的方法签名,而不是声明数据成员并初始化它们。
    【解决方案3】:

    好吧,当有“管道”、“操作序列”等时,首先想到的设计模式是责任链,如下所示

    并为您提供以下好处:

    1. 允许您在必要时(例如在运行时)添加新的处理程序,而无需修改其他处理程序和处理逻辑(SOLID 的开放/封闭原则)
    2. 允许处理程序在必要时停止处理请求
    3. 允许您将处理程序的处理逻辑彼此分离(SOLID 的单一职责原则)
    4. 允许您定义处理程序的顺序以在处理程序本身之外处理请求

    实际使用的一个例子是Servlet filters,您可以在其中调用doFilter(HttpRequest, HttpResponse, FilterChain) 来调用下一个处理程序

    protected void doFilter(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) {
        if (haveToInvokeNextHandler) {
            chain.doFilter(req, resp);
        }
    }
    

    如果使用经典的责任链模式,您的处理管道可能如下所示:

    API

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

    实施

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

    客户端代码

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

    此外,所有这些东西都可以通过删除相应的 next 引用来简化为彼此分离的处理程序链,以防您的处理管道必须始终调用所有可用步骤而不控制调用的必要性上一个:

    API

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

    客户端代码

    注意,在 Spring 框架的情况下(刚刚注意到 @Autowired 注释),客户端代码可能会更加简化,因为 @Autowired 注释可用于将相应类型的所有 bean 注入相应的集合中。

    documentation 声明如下:

    自动装配数组、集合和映射

    如果是数组、Collection 或 Map 依赖类型,容器会自动装配与声明的值类型匹配的所有 bean。为此,必须将映射键声明为 String 类型,该类型将解析为相应的 bean 名称。考虑到目标组件的 Ordered 和 @Order 值,将对此类容器提供的集合进行排序,否则将遵循它们在容器中的注册顺序。或者,单个匹配的目标 bean 也可能是一般类型的 Collection 或 Map 本身,因此被注入。

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

    【讨论】:

      猜你喜欢
      • 2012-02-09
      • 2017-02-18
      • 1970-01-01
      • 1970-01-01
      • 2019-09-12
      • 2017-12-03
      • 1970-01-01
      • 2014-01-06
      • 1970-01-01
      相关资源
      最近更新 更多