让数据流转换代码更加健壮流畅:List的Stream包装

为什么业务代码总是显得不够清晰 ? 因为开发人员总喜欢把业务与技术细节掺杂在一起。如下所示:

要改善这样的代码,需要将技术细节提取成可复用的基础库,而业务则可写成声明式的:

先创建一个 StreamUtil 工具类:


public class StreamUtil {
  public static <T,R> List<R> map(List<T> data, Function<T, R> mapFunc) {
    if (CollectionUtil.isEmpty(data)) { return new ArrayList();  }
    return data.stream().map(mapFunc).collect(Collectors.toList());
  }


  public static <T> List<T> filter(List<T> data, Predicate<T> filterFunc) {
    if (CollectionUtil.isEmpty(data)) { return new ArrayList();  }
    return data.stream().filter(filterFunc).collect(Collectors.toList());
  }


  public static <T,R> List<R> filterAndMap(List<T> data, Predicate<T> filterFunc, Function<T, R> mapFunc) {
    if (CollectionUtil.isEmpty(data)) { return new ArrayList();  }
    return data.stream().filter(filterFunc).map(mapFunc).collect(Collectors.toList());
  }
}

则原来 stream 流转换代码可写成:


rules = StreamUtil.filter(rules, rule -> BaselineUtils.matchPlatform(rule, platforms));
baseLineRules = StreamUtil.filter(rules, rule -> rule.getFamily() == BaselineRule.FAMILY_SYSTEM);


实际上, StreamUtil.filter 不如 rules.filter(testFunc) 更可读和流畅。如果能写成如下更好:


stream(rules).filter(rule -> BaselineUtils.matchPlatform(rule, platforms)); 

这需要对 List 做一个 Stream 的包装:

ListStream.java


/**
 * @Description 列表的 Stream 包装
 * @Date 2021/5/16 7:51 上午
 * @Created by qinshu
 */
public class ListStream<T> {

    private List<T> origin;

    public ListStream(List<T> list) {
        if (list == null) {
            this.origin = new ArrayList<>();
        }
        else {
            this.origin = list;
        }
    }

    public static <T> ListStream<T> stream(List<T> list) {
        return new ListStream<>(list);
    }

    public  <R> List<R> map(Function<? super T, R> func) {
        return origin.stream().map(func).collect(Collectors.toList());
    }

    public  <R> Set<R> mapToSet(Function<? super T, R> func) {
        return origin.stream().map(func).collect(Collectors.toSet());
    }

    public  List<T> filter(Predicate<? super T> predicate) {
        return origin.stream().filter(predicate).collect(Collectors.toList());
    }

    public <R> List<R> filterAndMapChain(List<Predicate<? super T>> beforeFilters,
                                                  Function<? super T,R> mapFunc, Predicate<R>... afterFilters) {
        Stream<T> stream = origin.stream();
        Stream<R> midResult = null;
        if (beforeFilters != null) {
            for (Predicate f: beforeFilters) {
                stream = stream.filter(f);
            }
        }
        if (mapFunc != null) {
            midResult = stream.map(mapFunc);
        }
        if (afterFilters != null) {
            for (Predicate<R> f: afterFilters) {
                midResult = midResult.filter(f);
            }
        }
        return midResult.collect(Collectors.toList());
    }


}


单测:


public class ListStreamTest {

    @Test
    public void testNull() {
        List<Integer> ints = stream(null).map(x -> (int)x + 1);
        Assert.assertEquals(ints.size(), 0);
    }

    @Test
    public void testMap() {
        List<Integer> ints = stream(Arrays.asList(1,2,3,4,5)).map(x -> x*2);
        Assert.assertArrayEquals(ints.toArray(new Integer[0]), new Integer[]{2,4,6,8,10});
    }

    @Test
    public void testFilter() {
        List<Integer> ints = stream(Arrays.asList(1,2,3,4,5)).filter(x -> x%2 ==0);
        Assert.assertArrayEquals(ints.toArray(new Integer[0]), new Integer[]{2,4});
    }

    @Test
    public void testFilterAndMapChain() {
        List<Integer> ints = stream(Arrays.asList(1,2,3,4,5)).filterAndMapChain(
                Arrays.asList(x -> x%2==1),
                x -> x*2,
                x -> x > 8);
        Assert.assertArrayEquals(ints.toArray(new Integer[0]), new Integer[]{10});
    }
}

这样,数据流转换代码就更加健壮和流畅。

很多人或许会觉得:把代码写得那么好图啥?其实,我是把代码表达当成一种思维的训练。 好代码不仅仅关乎习惯和态度,也关乎对设计和技术的理解。

posted @ 2021-05-16 08:38  琴水玉  阅读(202)  评论(0编辑  收藏  举报