命令式到函数式编程

逻辑判断和返回 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));
}

完毕。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 对各种值为"空"的情况处理不当,几乎是所有Bug的来源。 在我们的例子里,尽管tmp的值是nil,但调用tmp的r...
    AKyS佐毅阅读 10,559评论 1 13
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,957评论 19 139
  • Optional 本章内容 如何为缺失的值建模 Optional 类 应用Optional的几种模式 使用Opti...
    追憶逝水年華阅读 1,814评论 0 0
  • 现金辛苦辛苦,操你妈的骄傲辛苦![图片上传中...](file:///storage/emulated/0/Ten...
    撸撸俱乐部阅读 187评论 0 0
  • 脑子烧糊涂了啊喂 人生之幸福还包括 鼻塞之后通透的那一刻 汗像蒸出来的一样 细密 不能称之为水但却可以湿了浑身 可怕
    李大侠_阅读 138评论 0 0