命令式到函数式编程

逻辑判断和返回 vs. Optional

应用场景:当我们用到 if-elseif-else 的时候,可以考虑使用 Optional 语义。 举例说明:

if(id != null) {
    return find1(id);
} 

if(name != null) {
    return find2(name);
}

if(type != null) {
    return find3(type);
}

语义化

Supplier<?> chain = find(() -> find1(id), id)
                     .orElseGet(() -> find(() -> find2(name), name)
                       .orElse(() -> find3(type), type)));

Optional<Supplier<?>> find(Supplier s, Condtion... conditions) {
    return Stream.of(conditions).anyMatch(c -> c == null) ? Optional.empty() : Optinal.of(s);
}

去掉了不必要的Supplier,让代码清晰化

static Optional<Supplier<?>> or(Optional<Supplier<?>> hl, Optional<Supplier<?>> hr) {
    return hl.isPresent() ? hl : hr;
}

Supplier<?> chain =
                    or(
                        or(find(() -> find1(id), id),
                           find(() -> find2(name), name)),
                        find(() -> find3(type), type));     

进一步思考,上述的场景可以简化为: 如果值存在,那么拿来映射成另一个可能的存在;否则,返回不可能的存在。

ofNullalbe(id)
    .map(id -> find1(id))
    .map(r -> nonNull(r) ? r : find2(name)))
    .map(r -> nonNull(r) ? r : find3(type)))
    .orElse(T t);

假如我们有Optional.orGet函数,那就也可以这样实现

static <U> Optional<U> orGet(Supplier<? extends U> other) {
    return isPresent() ? this : Optional.ofNullable(other.get());
}

ofNullalbe(id)
    .map(id -> find1(id))
    .orGet(() -> find2(name))
    .orGet(() -> find3(type))
    .orElse(T t);

不过,上面的实现方式缺失了 if not null then else 的表达,进一步改进:

ofNullalbe(id)
    .map(id -> find1(id))
    .orGet(() -> ofNullable(name).map(name -> find2(name)))
    .orGet(() -> ofNullable(type).map(type -> find3(type)))
    .orElse(T t);
    
static <U> Optional<U> orGet(Supplier<? extends Optional<U>> other) {
    return isPresent() ? this : Optional.ofNullable(other.get());
}    

完毕。

语句重构到表达式

if-else -> Optional

Optional<Rule> rule = ruleOf(id);
if(rule.isPresent()) {
    return transform(rule.get());
} else {
    throw new RuntimeException();
}

public Rule transform(Rule rule) {
    return Rule.builder()
                .withName("No." + rule.getId())
                .build();
}

这是典型的语句可以重构到表达式的场景,关键是怎么重构呢? 第一步,调转if

Optional rule = ruleOf(id);

if(!rule.isPresent()) {
    throw new RuntimeException();
} 
   
return transform(rule.get());

第二步,Optional.map函数

...
return rule.map(r -> transform(r)).get();

第三步,inline transform函数

...
rule.map(r -> Rule.builder()
                    .withName("No." + r.getId())
                    .build()).get();

第四步,Optional.orElseThrow函数

...
rule.map(r -> Rule.builder()
                    .withName("No." + r.getId())
                    .build())
    .orElseThrow(() -> new RuntimeException());

第五步,注if释语句中的throw new RuntimeException()

if(!rule.isPresent()) {
   // throw new RuntimeException();
} 

这时候发现语句中为空,即可将整个语句删除。可以考虑inline rule

ruleOf(id).map(r -> Rule.builder()
                    .withName("No." + r.getId())
                    .build())
    .orElseThrow(() -> new RuntimeException());

完毕。

重复try...catch->Closure

// 结构性重复
if(meta.hasURI()) {
    try {
        return deciderOfURI.decide(attr);
    } catch (CustomizedException e) {
        //...
        LOGGER.error(e);
    } catch (Exception e) {
        //...
        LOGGER.error(e);
    }
}

if(meta.hasAction()) {
    try {
        return deciderOfAction.decide(attr);
    } catch (CustomizedException e) {
        //...
        LOGGER.error(e);
    } catch (Exception e) {
        //...
        LOGGER.error(e);
    }
}

这个结构性重复,一般想到的就是把表达式提成参数,但是由于表达式是预先求值的顺序,直接抽成参数意味着表达式求值时抛出的异常不能被捕获。所以使用functional parameter重构手法。 第一步,extract method

pubic Decision tryDecide() {
    if(meta.hasURI()) {
        try {
            return deciderOfURI.decide(attr);
        } catch (CustomizedException e) {
            //...
            LOGGER.error(e);
        } catch (Exception e) {
            //...
            LOGGER.error(e);
        }
    }
}

第二步,functional parameter

pubic Decision tryDecide(Supplier<Decision> decider) {
    if(meta.hasURI()) {
        try {
            return decider.get();
        } catch (CustomizedException e) {
            //...
            LOGGER.error(e);
        } catch (Exception e) {
            //...
            LOGGER.error(e);
        }
    }
}

第三步,替换重复

// 结构性重复
if(meta.hasURI()) {
    return tryDecide(() -> deciderOfAction.decide(attr));
}

if(meta.hasAction()) {
    return tryDecide(() -> deciderOfURI.decide(attr));
}

完毕。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Jerry的SAP技术分享

使用com.sun.imageio.plugins.png.PNGMetadata读取图片的元数据

所谓图片元数据,就是除了我们肉眼看到的图片内容外,隐藏在这些内容背后的一些技术数据。

1554
来自专栏LhWorld哥陪你聊算法

Hadoop源码篇--Reduce篇

Reduce文件会从Mapper任务中拉取很多小文件,小文件内部有序,但是整体是没序的,Reduce会合并小文件,然后套个归并算法,变成一个整体有序的文件。

2481
来自专栏码匠的流水账

聊聊storm的WindowedBolt

storm-2.0.0/storm-client/src/jvm/org/apache/storm/topology/IWindowedBolt.java

2314
来自专栏技术专栏

java程序 如何优雅地读取xml文件

9052
来自专栏SeanCheney的专栏

《Pandas Cookbook》第09章 合并Pandas对象

2031
来自专栏函数式编程语言及工具

SDP(9):MongoDB-Scala - data access and modeling

    MongoDB是一种文件型数据库,对数据格式没有硬性要求,所以可以实现灵活多变的数据存储和读取。MongoDB又是一种分布式数据库,与传统关系数据库不同...

3904
来自专栏前端说吧

正则表达式验证汇总

一、收集1  (转自https://blog.csdn.net/jumtre/article/details/13775351)

1101
来自专栏ml

位运算的方法,大结

Title:       位操作基础篇之位操作全面总结 Author:     MoreWindows E-mail:      morewindows@126...

6148
来自专栏小樱的经验随笔

POJ 2492 A Bug's Life

A Bug's Life Time Limit: 10000MS Memory Limit: 65536K Total Submissions:...

28910
来自专栏小樱的经验随笔

Code forces 719A Vitya in the Countryside

A. Vitya in the Countryside time limit per test:1 second memory limit per test:2...

3496

扫码关注云+社区

领取腾讯云代金券