美文网首页
命令式到函数式编程

命令式到函数式编程

作者: lambeta | 来源:发表于2017-11-22 13:26 被阅读46次

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

完毕。

相关文章

  • 《Kotlin入门实战》CH5 | 函数与函数式编程

    函数与函数式编程 函数式编程与命令式编程最大的不同是:函数式编程的焦点在于数据的映射,命令式编程(imperati...

  • 一:函数式编程:

    函数式与命令式编程的区别: 命令式编程关注的是怎么做,函数式编程关注的是做什么(由系统选择如何实现),命令式编程:...

  • RxJava系列|RxJava简介(一)

    函数响应式编程 函数式编程是一种编程范式。 常见的编程范式有:命令式编程、函数式编程和逻辑式编程。 面向对象就是一...

  • 函数式编程与scala 入门

    scala 预备知识 命令式编程 和 函数式编程函数式编程关心类型(代数结构)之间的关系命令式编程关心解决问题的步...

  • 你真的理解面向对象吗?

    面向对象 我们常见的编程范式有命令式编程,函数式编程,逻辑式编程,而面向对象编程是一种命令式编程。 命令式编程是面...

  • Java修炼笔记之函数式编程

    函数式编程简介 函数式编程是一种编程范式,常见的编程范式还有命令式编程和逻辑式编程,其中命令式编程是对计算机硬件的...

  • RxJava2

    函数式编程是一种编程范式。我们常见的编程范式有命令式编程、函数式编程和逻辑式编程。我们常见的面向对象编程是一种命令...

  • 编程范式:命令式编程(Imperative)、声明式编程(Dec

    主要的编程范式有三种:命令式编程,声明式编程和函数式编程。 命令式编程: 命令式编程的主要思想是关注计算机执行的步...

  • 编程范式:命令式编程(Imperative)、声明式编程(Dec

    主要的编程范式有三种:命令式编程,声明式编程和函数式编程。 命令式编程: 命令式编程的主要思想是关注计算机执行的步...

  • 函数式编程

    主要的编程范式有三种:命令式编程,声明式编程和函数式编程。 命令式编程: 命令式编程的主要思想是关注计算机执行的步...

网友评论

      本文标题:命令式到函数式编程

      本文链接:https://www.haomeiwen.com/subject/pjbnvxtx.html