java

Optional的详细实战用法,优雅的消除if..else..复

2021-04-12  本文已影响0人  coder_鬼彻

怎么优雅的让你的代码不再有if...else;如何优雅的躲避深恶痛绝的NPE,这也许是一个程序员一生的追求,今天给大家带来一个案例,将一个臃肿的胖子,P成一个高富帅的过程.
其中使用到了J8的Optional这个容器.当然肯定有小伙伴比我用的好,或者比我理解的更透彻,我写的有不对的地方,也请小伙伴指出来,大家共同学习,共同进步..

if(我是高富帅){
    System.out("对象排排站")
}else{
    System.out("苦逼的写代码")
}

不叨逼叨逼叨了.先给大家看段代码,你若不晕,我连吹两瓶二锅头.....

public class DiscountedPriceConverter implements CustomConverter {
    @Override
    public Object convert(Object destination, Object source, Class<?> destinationClass, Class<?> sourceClass) {
        double platformDiscount = 0;
        if (ObjectUtil.isEmpty(source)) {
            return String.valueOf(platformDiscount);
        }
        if (ObjectUtil.isNotEmpty(source) && source instanceof List) {
            List<?> objectList = (List<?>) source;
            Optional<?> optional = objectList.stream().findFirst();
            if (optional.isPresent()) {
                Object object = optional.get();
                if (object instanceof CouponDetail) {
                    List<CouponDetail> couponDetails =
                            ListsUtil.castList(source, CouponDetail.class);
                    if (ObjectUtil.isNotEmpty(couponDetails)) {
                        assert couponDetails != null;
                        for (CouponDetail couponDetail : couponDetails) {
                            if (ObjectUtil.isEmpty(couponDetail)) {
                                platformDiscount = 0;
                            }
                            if (ObjectUtil.isNotEmpty(couponDetail) && ObjectUtil.isNotEmpty(couponDetail.getCouponPrice())) {
                                platformDiscount += Double.parseDouble(couponDetail.getCouponPrice());
                            }
                        }
                        return String.valueOf(platformDiscount);
                    }
                }

                if (object instanceof com.jd.open.api.sdk.domain.order.OrderQueryJsfService.response.enGet
                        .CouponDetail) {
                    List<com.jd.open.api.sdk.domain.order.OrderQueryJsfService.response.enGet
                            .CouponDetail> couponDetails =
                            ListsUtil.castList(source, com.jd.open.api.sdk.domain.order.OrderQueryJsfService.response.enGet
                                    .CouponDetail.class);
                    if (ObjectUtil.isNotEmpty(couponDetails)) {
                        assert couponDetails != null;
                        for (com.jd.open.api.sdk.domain.order.OrderQueryJsfService.response.enGet
                                .CouponDetail couponDetail : couponDetails) {
                            if (ObjectUtil.isEmpty(couponDetail)) {
                                platformDiscount = 0;
                            }
                            if (ObjectUtil.isNotEmpty(couponDetail) && ObjectUtil.isNotEmpty(couponDetail.getCouponPrice())) {
                                platformDiscount += Double.parseDouble(couponDetail.getCouponPrice());
                            }
                        }
                        return String.valueOf(platformDiscount);
                    }
                }
            }
            return String.valueOf(platformDiscount);
        }
        return String.valueOf(platformDiscount);
    }
}

看了这段代码,如果你看到这段代码,如果没有反胃的生理反应,那么恭喜你,蓝翔挖掘机专业是你改变人生的最好途径.接下来,咱们就改造这个代码,让他看起来高大上...(这段没有注释的代码)

  1. 业务梳理

①、首先我们先看这里的大框架,是由两个两个逻辑块, 判空逻辑; 如果source为空则返回默认值,如果source不为空则返回计算后的值。
②、当source不为空时,需要判断source的类型是否为List类型,若不是则直接返回默认值.(这里受业务约束,必须是list)
③、当source为list且不为空时,要确定list中的泛型(这里受业务约束,list中的泛型为同一泛型,不存在多种类型同时存在的情况)
④、当source不为空时,这里执行了两个业务逻辑判断,也就是下面两个类的判断,两个类走了不同转换逻辑.否则返回默认值

com.jd.open.api.sdk.domain.order.OrderQueryJsfService.response.enGet.CouponDetail
com.jd.open.api.sdk.domain.order.OrderQueryJsfService.response.search.CouponDetail
  1. 代码构建

通过对代码结构逻辑的梳理我们就可以着手改造这段代码了,首先我们先把大的框架写出来,在进行具体业务的逻辑处理

//这里为空则返回空对象 这里表示 如果source不为空则继续执行,若为空直接返回默认值
Optional.ofNullable(source)
         //若source为list类型,这继续执行,若不为list类型则直接返回默认值
         .filter(fSource -> fSource instanceof List)
         //若source为list类型执行计算
         .map(mSource -> {
             //这里进行计算或者下一步逻辑
         })
         //这里返回默认值
         .orElse(String.valueOf(platformDiscount));

由上面代码可以看出这段逻辑代码的框架逻辑我们就已经搭建好了,下面对代码进行技术分析:(看黑板)

2.1、由于不知道source是否为空,这里使用==ofNullable()==,进行Optional的创建。Optional有三种实例化的方式:

①创建空Optional对象 empty();
②创建非空Optional对象,of();这里的容器中的对象一定不能为空,否者会抛出异常;
③创建非空或空Optional对象,ofNullable();

2.2、使用filter()进行source对象是否是List集合; filter()条件判断,返回optional容器,若结果为true则返回带有对象的Optional容器,否则返回空容器;

2.3、使用map()对内部细节业务逻辑进行计算;这里不对map()方法进行详细分析了,网上有很多资料,大家可以自己去看;这里给大家总结几点重要的点:(看黑板)

①map()接收的是上一层逻辑返回的容器中的对象值(类型),都必须一致;否则无法使用map()方法
②map()方法是主动为传递过来的对象创建Optional容器的,这跟flatMap()方法又却别;
③map()方法首先对容器内的对象进行判断,若对象为空,则直接返回空的Optional容器对象,若不为空,则直接将计算后的值重新创建新的Optional容器进行返回

2.4、orElse()方法返回默认值,Optional中的这个方法,就可以认为这个方法就是返回默认值的,可以理解为,如果为空,则直接返回默认值,不为空,则返回计算值;(这里需要强调,orElse()方法,在执行逻辑中,始终都是执行的,不管你当前Optional容器中的对象是不是空的,都会执行orElse()方法中的逻辑;如果为空执行,不为空不执行,需要使用orElseGet()方法);


接下来我们填充细节逻辑:

//这里为空则返回空对象 这里表示 如果source不为空则继续执行,若为空直接返回默认值
Optional.ofNullable(source)
         //若source为list类型,这继续执行,若不为list类型则直接返回默认值
         .filter(fSource -> fSource instanceof List)
         //若source为list类型执行计算
         .map(mSource -> {
            //这里确定source为list类型,则直接强转list,因为不确定泛型,所以用?来表示
             List<?> objectList = (List<?>) source;
            //取出list的第一个元素,这里无法得知,list中的元素是否为空,
            //或者其元素的中的属性值是否为空,所以还需要使用Optional容器进行规避NPE,
            //findFirst()方法返回的就是一个Optional容器 所以我们直接使用Map进行计算
            return objectList
                    .stream()
                    .findFirst()
                    //此时map中的firstSource为list中的第一条元素对象;
                    .map(firstSource ->
                    //这里需要强调,由于Optional中的对象不能同级进行传递,上面技术分析中有说到这一点;                            
                        FunctionBuilder
                                    //创建IF函数实例
                                    .returnIf(new HashMap<Object, FunctionReturn<String>>())
                                    //添加函数条件以及执行函数
                                    .addReturn(CouponDetail.class, () -> ifSearchCouponDetail(source))
                                    //添加函数条件以及执行函数
                                    .addReturn(com.jd.open.api.sdk.domain.order
                                            .OrderQueryJsfService.response.enGet
                                            .CouponDetail.class, () -> ifEnGetCouPonDetail(source))
                                    //实际入参对象
                                    .doIfInstance(firstSource)
                                    //默认值
                                    .defaultValue(null)
                    ).orElse(null);
         })
         //这里返回默认值
         .orElse(String.valueOf(platformDiscount));

将具体的业务代码抽离成方法.

ifEnGetCouPonDetail(),如果是EnGetCouPonDetail类型走此方法执行业务
ifSearchCouponDetail(),如果是SearchCouponDetail类型的走此方法执行业务

   @NotNull
    private String ifEnGetCouPonDetail(Object source) {
        List<com.jd.open.api.sdk.domain.order.OrderQueryJsfService.response.
                enGet.CouponDetail> couponDetails =
                ListsUtil.castList(source, com.jd.open.api.sdk.domain.order.
                        OrderQueryJsfService.response.enGet.CouponDetail.class);
        double optionalDiscount = 0.0;
        for (com.jd.open.api.sdk.domain.order
                .OrderQueryJsfService.response.enGet
                .CouponDetail couponDetail : couponDetails) {
            optionalDiscount += Optional.ofNullable(couponDetail)
                    .map(com.jd.open.api.sdk.domain.order.OrderQueryJsfService.
                            response.enGet.CouponDetail::getCouponPrice)
                    .map(Double::parseDouble)
                    .orElse(optionalDiscount);
        }
        return String.valueOf(optionalDiscount);
    }
 @NotNull
    private String ifSearchCouponDetail(Object source) {
        List<CouponDetail> couponDetails =
                ListsUtil.castList(source, CouponDetail.class);
        double optionalDiscount = 0.0;
        for (CouponDetail couponDetail : couponDetails) {
            optionalDiscount += Optional.ofNullable(couponDetail)
                    .map(CouponDetail::getCouponPrice)
                    .map(Double::parseDouble)
                    .orElse(optionalDiscount);
        }
        return String.valueOf(optionalDiscount);
    }

对以上代码技术点进行分析:

在以上代码看来.逻辑清晰,结构简单,可阅读性强;这里重点说一下FunctionBuilder这个函数逻辑运算,这里是我自己封装的复杂逻辑运算的执行器,这里不能完全使用Optional来消除if,原因有两点如下:

①、Optional只能对容器中的对象是否为空,做为条件进行逻辑计算,但不能对其他条件逻辑进行判断
②、Optional对同级逻辑是不能使用同一个对象的, 比如你使用了两个map()方法,第一个map中是User对象,第二个map()方法中的Optional容器中的对象是第一个map(),计算后返回的对象,不可能是同一个User对象了,很明显不符合这里的业务逻辑;


这里我也贴出封装的部分源码

FunctionBuilder类

/**
 * @Author: yangjiahui
 * @Description: TODO  构建if函数的创建工具类
 * @Date: 2021/03/22 14:15
 */
public class FunctionBuilder {
    /**
     * 创建无返回值if函数
     *
     * @param map Map<条件,执行函数>
     * @param <K> 条件参数类型
     * @return if函数实例
     * @see Map
     */
    public static <K> IfVoidFunction<K> voidIf(Map<K, Function> map) {
        return IfVoidFunction.<K>builder().buildVoidIf(map).build();
    }

    public static <K, T> IfFunctionReturn<K, T> returnIf(Map<K, FunctionReturn<T>> returnMap) {
        return IfFunctionReturn.<K, T>builder().buildReturnIf(returnMap).build();
    }
}

IfFunctionReturn类

这里是带有返回值的if函数

/**
 * @Author: yangjiahui
 * @Description: TODO 带有返回值的if函数实例
 * @Date: 2021/03/05 5:43 下午
 */
public class IfFunctionReturn<K, T> {

    private Map<K, FunctionReturn<T>> mapReturn;

    /**
     * 如果不为null,则为该值;
     * 否则为false。如果为null,则表示不存在任何值
     */
    private T result;


    public void setMap(Map<K, FunctionReturn<T>> mapReturn) {
        this.mapReturn = mapReturn;
    }

    public IfFunctionReturn() {
    }

    /**
     * 添加条件 有返回值函数
     *
     * @param key            需要验证的条件(key)
     * @param functionReturn 要执行的方法
     * @return this.
     */
    public IfFunctionReturn<K, T> addReturn(K key, FunctionReturn<T> functionReturn) {
        this.mapReturn.put(key, functionReturn);
        return this;
    }

    /**
     * 批量添加条件 有返回值函数
     *
     * @param key            需要验证的条件(key)
     * @param functionReturn 要执行的方法
     * @return this.
     */
    @SafeVarargs
    public final IfFunctionReturn<K, T> addReturnAll(FunctionReturn<T> functionReturn, K... key) {
        for (K element : key) {
            if (ObjectUtil.isNotEmpty(element)) {
                this.mapReturn.put(element, functionReturn);
            }
        }
        return this;
    }

    /**
     * 确定key是否存在,如果存在,则执行value中的函数。
     * <p>
     * 函数有返回值
     * <p>
     * 若key为对象类型 则需重写 equal方法和hashcode方法
     * key值和map中的key值必须一致
     *
     * @param key the key need to verify
     */

    public IfFunctionReturn<K, T> doIfEqualReturn(@NotNull K key) {
        if (this.mapReturn.containsKey(key)) {
            this.result = mapReturn.get(key).invokeReturn();
            return this;
        }
        return this;
    }

    /**
     * 确定key是否存在,如果存在,则执行value中的函数。若不存在执行默认函数
     * <p>
     * 函数无返回值 增加默认执行函数 若传入条件皆不符合 则执行默认函数
     * <p>
     * 若key为对象类型 则需重写 equal方法和hashcode方法
     * key值和map中的key值必须一致
     *
     * @param key the key need to verify 条件值
     */
    public IfFunctionReturn<K, T> doIfEqualReturn(@NotNull K key, @NotNull FunctionReturn<T> defaultFunction) {
        boolean doesItContain = this.mapReturn.containsKey(key);
        if (doesItContain) {
            this.result = mapReturn.get(key).invokeReturn();
            return this;
        }
        this.result = defaultFunction.invokeReturn();
        return this;
    }

    /**
     * 比较对象类型是否一致 若一致则执行函数
     * <p>
     * 注意:此方法仅支持 同一个classloader加载两个类使用
     * <p>
     * 函数无返回值
     *
     * @param key the key need to verify 条件值
     */
    public IfFunctionReturn<K, T> doIfInstance(@NotNull K key) {
        mapReturn.forEach((setKey, value) -> {
            if (setKey.equals(key.getClass())) {
                this.result = value.invokeReturn();
            }
        });
        return this;
    }

    /**
     * 比较对象类型是否一致 若一致则执行函数 若不一致 执行默认函数
     * <p>
     * 注意:此方法仅支持 同一个classloader加载两个类使用
     * <p>
     * 函数无返回值 增加默认执行函数 若传入条件皆不符合 则执行默认函数
     *
     * @param key the key need to verify 条件值
     */
    public IfFunctionReturn<K, T> doIfInstance(@NotNull K key, @NotNull FunctionReturn<T> defaultFunction) {

        boolean execution = true;
        for (Map.Entry<K, FunctionReturn<T>> entry : mapReturn.entrySet()) {
            if (entry.getKey().equals(key.getClass())) {
                this.result = entry.getValue().invokeReturn();
                execution = false;
            }
        }
        if (execution) {
            this.result = defaultFunction.invokeReturn();
        }
        return this;
    }

    /**
     * 获取当前函数 返回值
     * <p>
     * 警告: 返回值可能为 null
     *
     * @return 返回对象
     */
    public T get() {
        if (result == null) {
            throw new NoSuchElementException("返回值对象为空!");
        }
        return result;
    }

    public T defaultValue(T other) {
        return result != null ? result : other;
    }
    /**
     * 执行完毕之后自动刷新 map 防止出现数据冲突
     */
    public IfFunctionReturn<K, T> refresh() {
        this.mapReturn.clear();
        return this;
    }

    /**
     * 创建桥接实例
     *
     * @param <K>条件类型泛型
     * @param <T>返回值类型泛型
     */
    public static <K, T> ResIfFunctionReturn<K, T> builder() {
        return new ResIfFunctionReturn<>();
    }

    public static class ResIfFunctionReturn<K, T> {
        private Map<K, FunctionReturn<T>> mapReturn;

        private ResIfFunctionReturn() {
        }

        public ResIfFunctionReturn<K, T> buildReturnIf(Map<K, FunctionReturn<T>> mapReturn) {
            this.mapReturn = mapReturn;
            return this;
        }

        public IfFunctionReturn<K, T> build() {
            IfFunctionReturn<K, T> functionReturn = new IfFunctionReturn<>();
            functionReturn.setMap(mapReturn);
            return functionReturn;
        }
    }
}

FunctionReturn函数接口

用于执行业务逻辑的接口封装


/**
 * @Author: yangjiahui
 * @Description: TODO 有返回值执行函数
 * @Date: 2020/12/22 4:20 下午
 */
@FunctionalInterface
public interface FunctionReturn<T> {
    /**
     * 有返回值的函数
     * @return
     */
    T invokeReturn();
}

其实这个if函数封装很简单,不过多的赘述。


三、复盘

接下来我们看下优化前的代码 和优化后的;

优化前:11个if判断逻辑(代码进行了业务删减)

public Object convert(Object destination, Object source, Class<?> destinationClass, Class<?> sourceClass) {
        if (ObjectUtil.isEmpty(source)) {}
        if (ObjectUtil.isNotEmpty(source) && source instanceof List) {
            if (optional.isPresent()) {
                if (object instanceof CouponDetail) {
                    if (ObjectUtil.isNotEmpty(couponDetails)) {
                            if (ObjectUtil.isEmpty(couponDetail)) {}
                            if (ObjectUtil.isNotEmpty(couponDetail){}
                        }
                        return String.valueOf(platformDiscount);
                    }
                }
                if (object instanceof com...enGet.CouponDetail) {
                    if (ObjectUtil.isNotEmpty(couponDetails)) {
                            if (ObjectUtil.isEmpty(couponDetail)) {}
                            if (ObjectUtil.isNotEmpty(couponDetail)
                        }
                        return String.valueOf(platformDiscount);
                    }
                }
            }
            return String.valueOf(platformDiscount);
        }
        return String.valueOf(platformDiscount);
    }

优化后:

Optional.ofNullable(source)
         .filter(fSource -> fSource instanceof List)
         .map(mSource -> {
             List<?> objectList = (List<?>) source;
            return objectList
                    .stream()
                    .findFirst()
                    .map(firstSource -> 
                        FunctionBuilder
                                    .returnIf(new HashMap<Object, FunctionReturn<String>>())
                                    .addReturn(CouponDetail.class, () -> ifSearchCouponDetail(source))
                                    .addReturn(com...enGet.CouponDetail.class, () -> ifEnGetCouPonDetail(source))
                                    .doIfInstance(firstSource)
                                    .defaultValue(null)
                    ).orElse(null);
         })
         .orElse(String.valueOf(platformDiscount));

优点不用多讲....一幕了然呀!!!!!!

敲代码容易,思想不易,转载请标注出处,谢谢诸神...

江山父老能容我,不使人间造孽钱.

上一篇下一篇

猜你喜欢

热点阅读