Java流水线Pipeline设计模式

开发 前端
Pipeline模式适用于流式递归传递输入和处理后的输出,对于比较简单的场景,使用Java函数接口是挺不错的选项。

概述

管道模式背后的主要思想是创建一组操作(管道)并通过它传递数据。跟责任链和装饰器模式相比,Pipeline的主要优势在于它对结果的类型具有灵活性。

管道可以处理任何类型的输入和输出。

不可变管道

让我们创建一个不可变的管道的例子。从管道接口开始:

public interface Pipe<IN, OUT> {
    OUT process(IN input);
}

这是一个非常简单的接口,只有一个方法,它接受输入并产生输出。接口是参数化的,我们可以在其中提供任何实现。

现在,让我们创建一个管道类:

public class Pipeline<IN, OUT> {

    private Collection<Pipe<?, ?>> pipes;

    private Pipeline(Pipe<IN, OUT> pipe) {
        pipes = Collections.singletonList(pipe);
    }

    private Pipeline(Collection<Pipe<?, ?>> pipes) {
        this.pipes = new ArrayList<>(pipes);
    }

    public static <IN, OUT> Pipeline<IN, OUT> of(Pipe<IN, OUT> pipe) {
        return new Pipeline<>(pipe);
    }

    public <NEW_OUT> Pipeline<IN, NEW_OUT> withNextPipe(Pipe<OUT, NEW_OUT> pipe) {
        final ArrayList<Pipe<?, ?>> newPipes = new ArrayList<>(pipes);
        newPipes.add(pipe);
        return new Pipeline<>(newPipes);
    }

    public OUT process(IN input) {
        Object output = input;
        for (final Pipe pipe : pipes) {
            output = pipe.process(output);
        }
        return (OUT) output;
    }
}

因为我们需要一个类型安全级别,并且不允许使管道失效,所以在添加新管道时,将产生一个新的独立管道。

简单管道

我们可以稍微简化上面的例子,并完全去掉Pipeline类:

public interface Pipe<IN, OUT> {
    OUT process(IN input);

    default <NEW_OUT> Pipe<IN, NEW_OUT> add(Pipe <OUT, NEW_OUT> pipe) {
        return input -> pipe.process(process(input));
    }
}

与以前使用管道的实现相比,此解决方案非常简单和灵活。

改进

我们可以用现有的Function接口替代它:

public interface Function<T, R> {
    //...
    R apply(T t);
    //...
}

此外,Function接口包含两个有用的方法,其中一个是andThen:

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
    Objects.requireNonNull(after);
    return (T t) -> after.apply(apply(t));
}

我们可以使用它来代替以前的add方法。此外,Function接口提供了一种在管道开始时添加函数的方法:

default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
    Objects.requireNonNull(before);
    return (V v) -> apply(before.apply(v));
}

通过使用Function,我们可以创建非常灵活和易于使用的管道:

@Test
void whenCombiningThreeFunctions_andInitializingPipeline_thenResultIsCorrect() {
    Function<Integer, Integer> square = s -> s * s;
    Function<Integer, Integer> half = s -> s / 2;
    Function<Integer, String> toString = Object::toString;
    Function<Integer, String> pipeline = square.andThen(half)
        .andThen(toString);
    String result = pipeline.apply(5);
    String expected = "12";
    assertEquals(expected, result);
}

我们可以使用BiFunctions扩展管道:

@Test
void whenCombiningFunctionAndBiFunctions_andInitializingPipeline_thenResultIsCorrect() {
    BiFunction<Integer, Integer, Integer> add = Integer::sum;
    BiFunction<Integer, Integer, Integer> mul = (a, b) -> a * b;
    Function<Integer, String> toString = Object::toString;
    BiFunction<Integer, Integer, String> pipeline = add.andThen(a -> mul.apply(a, 2))
        .andThen(toString);
    String result = pipeline.apply(1, 2);
    String expected = "6";
    assertEquals(expected, result);
}

因为andThen方法采用Function,所以我们必须将mul BiFunction转换为一个Function。

结论

Pipeline模式适用于流式递归传递输入和处理后的输出,对于比较简单的场景,使用Java函数接口是挺不错的选项。

责任编辑:武晓燕 来源: 今日头条
相关推荐

2024-01-07 12:47:35

Golang流水线设计模式

2021-11-08 07:41:16

Go流水线编程

2017-03-02 14:12:13

流水线代码Clojure

2017-02-28 16:00:45

DevOpsMarkdownreST

2022-07-18 06:05:28

Gitlab流水线

2017-02-28 15:40:30

Docker流水线Azure

2013-06-06 09:31:52

2021-12-17 18:21:54

大数据流水线设计

2020-06-16 10:20:32

JavaStream流水线

2021-06-26 14:22:34

Tekton流水线Kubernetes

2022-01-26 08:12:42

Jenkins开源流水线

2023-08-18 10:24:52

GitLabCI 流水线

2021-06-28 06:32:46

Tekton Kubernetes Clone

2021-06-18 05:48:02

Tekton DevopsKubernetes

2021-12-24 08:02:48

GitLabCI模板库流水线优化

2023-09-27 08:24:49

2011-10-19 08:04:12

2017-03-15 10:08:26

软件开发流水线

2018-10-23 16:35:19

华为云

2012-04-19 11:44:52

iPhone
点赞
收藏

51CTO技术栈公众号