Java 流水线设计模式的实现

Java 流水线设计模式的实现,java,oop,design-patterns,design-principles,java-8,Java,Oop,Design Patterns,Design Principles,Java 8,这是一个关于管道实现的设计问题。下面是我天真的实现 管道中各个步骤/阶段的接口: public interface Step<T, U> { public U execute(T input); } public class StepOne implements Step<Integer, Integer> { @Override public Integer execute(Integer input) { return inp

这是一个关于管道实现的设计问题。下面是我天真的实现

管道中各个步骤/阶段的接口:

public interface Step<T, U> {
    public U execute(T input);
}
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;
    }
}
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();
    } 
}
但是,正如您所看到的,Naiver实现有许多局限性

其中一个主要问题是,由于需求是每个步骤的输出可以是任何类型,所以朴素的实现不是类型安全的(管道类中的execute方法)。如果我碰巧将管道中的步骤连接错误,应用程序将失败

有谁能帮我设计解决方案,添加到我已经编写的代码中,或者给我指出一个已经存在的模式来解决这个问题吗?

我将重点介绍

如果我碰巧将管道中的步骤连接错误,应用程序将失败

是的,这是个问题<代码>第三步是这里的陌生人。我不认为一个简单的模式可能有用,我确实认为它必须是策略和构建器模式的组合。例如:

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
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;
Pipeline intPipe=新管道();
intPipe=intPipe.add(新的StepOne());//增量100
intPipe=intPipe.add(新的第二步());//增量500
管道strPipe=intPipe.add(新的StepThree());//转换
管道是这样的:

public static class Pipeline<IN, OUT> {
   //...
   public<A> Pipeline<OUT,A> add(Step<IN,A> step) {
     pipelineSteps.add(step);
     return (Pipeline<OUT,A>)this;
   }
}
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;
    }
}
公共静态类管道{
//...
公共管道添加(步骤){
管道步骤。添加(步骤);
返回(管道)这个;
}
}
使用fast builder语法,这可能会起作用:

Pipeline<String, Integer> pipe = new Pipeline<Integer, Integer>()
    .add(new StepOne()).add(new StepTwo()).add(new StepThree());
管道=新管道() .add(新步骤一()).add(新步骤二()).add(新步骤三());
这应该是可行的,因为泛型不是字节码的一部分。

您的方法非常好。但是,我会这样编写管道类:

public static class Pipeline<IN, OUT> {
   //...
   public<A> Pipeline<OUT,A> add(Step<IN,A> step) {
     pipelineSteps.add(step);
     return (Pipeline<OUT,A>)this;
   }
}
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;
    }
}
公共类管道{
private List pipelineSteps=new ArrayList();
私有对象firstStepInput=100;
公共管道(){
添加(新的StepOne());
添加(新的步骤二());
添加(新的StepThree());
}
public void execute(){
用于(步骤:pipelineSteps){
对象输出=步骤执行(第一步输入);
第一步输入=输出;
}
}
公共字符串getResult(){
返回(字符串)第一步输入;
}
}
这样,所有特定的步骤知识都封装在管道类中


在这种情况下,execute方法可以执行循环。但是,如果需要,execute类可以逐个执行这些步骤。

为什么需要额外的
管道类?我想你可以除掉中间人。这将使api更简单,例如:

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
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;
Step source=Step.of(Object::toString);
步骤toHex=source.pipe(it->Integer.parseInt(it,16));
toHex.execute(11/*0x11*/);//返回17;
您可以按如下方式简单地实现管道模式:

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;
    }
}
接口步骤{
O执行(I值);
默认阶梯管道(阶梯源){
返回值->source.execute(执行(值));
}
静态步进(步进源){
返回源;
}
}
在以前的java版本中,您可以使用抽象类:

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;
    }
}
抽象静态类步骤{
公开摘要O执行(I值);
公用阶梯管道(阶梯源){
返回新步骤(){
@凌驾
公共R执行(I值){
返回source.execute(Step.this.execute(value));
}
};
}
公共静态步骤(步骤源){
返回源;
}
}
您基本上可以使用

公共类管道{
private List pipelineSteps=new ArrayList();
私有对象firstStepInput=100;
公共管道(){
添加(新的StepOne());
添加(新的步骤二());
添加(新的StepThree());
}

您不需要为此创建新接口

Java8已经有一个名为Function的函数接口,它允许您创建函数链(换句话说,您的管道)

Function addOne=it->{
系统输出打印项次(it+1);
返回+1;
};
函数addTwo=it->{
系统输出打印项次(it+2);
返回+2;
};
功能时间2=输入->{
系统输出打印项次(输入*2);
返回输入*2;
};
最终功能管道=添加一个
.第二次(第二次)
.第二(加两);
管道。应用(10);

如果你想更多地了解函数接口:

指向引用模式的链接。谢谢@ NekBeor指向论文。然而,通过本文,我无法理解流水线是如何设计的,以便它可以处理不同输出类型的阶段/步骤。/对于您所述的示例。请参见此处,感谢Nick提供的链接。此处提供的示例帮助很大。@NickBell链接已关闭感谢您指出如何正确封装管道类。感谢Peter回答问题!:)@PrashantChauhan随时为您服务;DBy decents,低估的答案!这是amazing、 你能解释一下
Step.of(Object::toString)
是如何工作的吗?
toString
是如何被解释为
Step
?刚刚在一个应用程序中实现了这一点,代码很少,但效果很好。@z0r:它与
Step.of(input->input.toString())是一样的;
运算符是引用类的方法的lambda表达式。请格式化(编写答案时有一个预览窗口)解释你的答案(&D)。你确实想让人们理解你做了什么,不是吗?我们可以在算法中也有一个抽象方法,并强制用户实现该方法。例如:抽象函数userDefined;final Function pipe=sourceInt.and then(userDefined)。and()…投票赞成使用内置interfa