让数据流转换代码更加健壮流畅: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});
}
}
这样,数据流转换代码就更加健壮和流畅。
很多人或许会觉得:把代码写得那么好图啥?其实,我是把代码表达当成一种思维的训练。 好代码不仅仅关乎习惯和态度,也关乎对设计和技术的理解。