用函数式写法精简 Java 代码的一个例子

用户头像
Sean
关注
发布于: 2020 年 09 月 06 日
用函数式写法精简Java代码的一个例子

在写代码的时候,经常遇到做一些判断,并在不同条件下执行不同操作。java中还会经常判断一个对象是否为null,并针对是和否两种情况分别进行处理。



一下用一个小例子说明,同一个逻辑,用函数式和非函数式写出来是什么样子。之后简要地探讨了二者的区别。这个例子是实际中遇到。我们有不同类别的文档索引在elasticsearch中,如果指定了具体的类别,则用该类别对应的indexer返回的查询语句。如果没有指定类别或者有多个类别,则默认在全部类别搜索。

类别和其对应的Indexer保存在indexerMap,categories是用户传进来的类别列表。

// 如果只有一个类目,则从indexerMap中找出该类目对应的indexer并得到searchBuilder。
// 如果不存在对应类目的indexer或者indexer返回来的searchBuilder为空,则用默认的queryAllBuilder
List<String> categories = ...
Map<String, DocIndexer> indexerMap = ...
// 函数式的写法
final SearchSourceBuilder searchSourceBuilder = Option.of(categories)
.filter(it -> it.size() == 1)
.map(it -> it.get(0))
.flatMap(it -> Option.of(indexerMap.get(it)))
.flatMap(it -> Option.of(it.searchBuilder()))
.getOrElseLazy(this::queryAllBuilder);
// 非函数式写法
SearchSourceBuilder searchSourceBuilder;
if (categories.size() == 1) {
DocIndexer indexer = indexerMap.get(categories.get(0));
if (indexer == null) {
searchSourceBuilder = this.queryAllBuilder();
} else {
SearchSourceBuilder b = indexer.searchBuilder();
searchSourceBuilder = Objects.requireNonNullElseGet(b, this::queryAllBuilder);
}
} else {
searchSourceBuilder = this.queryAllBuilder();
}



函数式的写法,有如下好处:

  • 紧凑。整个执行流依次往下,如果执行途中有异常,则算子函数会自动处理。因此,写逻辑的时候,只需要关注happy path即可。即便执行过程中出现了异常,需要用一个替代方案挽救,也就只是多一个算子而已。

  • 临时变量不见了。同样的逻辑,非函数写法需要定义各种临时变量来存储中间结果。多多的临时变量,不够优雅。

  • null不见了。任何没值的情况都可以看做是异常。将异常的处理隐匿于算子之后,让视野所见的代码干净整洁。

函数式和非函数式,是两种不同的思考方式,也是两种不同的语言(如英语和汉语之类)。同样的事务,二者都可以描述,但描述的优雅程度不同。熟悉函数式的思考方式之后,人的思维也会潜移默化地被塑造,思考问题也开始变得函数式起来。



算子(map、flatMap以及filter等)把一些常见的逻辑事先实现出来,等到写具体逻辑的时候,只需要简单的拼凑即可完成逻辑的编写了。



受函数式语言的启发,我实现了一个java版的函数式lib。其中Option对象有两个子类,Some和None,分别代表有值和无值。java自带的Optional也有类似功能,但当处理过程中有异常时,Optional并不记录这种异常。因此,我在自己的Option版本中,加入了异常信息,让None可以携带Exception从执行流的上游流到下游。其中,也对异步编程做了一点抽象,值得体验一下。

发布于: 2020 年 09 月 06 日 阅读数: 630
用户头像

Sean

关注

还未添加个人签名 2019.04.09 加入

还未添加个人简介

评论 (1 条评论)

发布
用户头像
文章被推荐了,请添加封面图,跟美观以及利于点击
2020 年 09 月 07 日 15:02
回复
没有更多了
用函数式写法精简Java代码的一个例子