美文网首页java
Optional的详细实战用法,优雅的消除if..else..复

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

作者: coder_鬼彻 | 来源:发表于2021-04-12 14:27 被阅读0次

    怎么优雅的让你的代码不再有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));
    

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

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

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

    相关文章

      网友评论

        本文标题:Optional的详细实战用法,优雅的消除if..else..复

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