怎么优雅的让你的代码不再有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);
}
}
看了这段代码,如果你看到这段代码,如果没有反胃的生理反应,那么恭喜你,蓝翔挖掘机专业是你改变人生的最好途径.接下来,咱们就改造这个代码,让他看起来高大上…(这段没有注释的代码)
①、首先我们先看这里的大框架,是由两个两个逻辑块, 判空逻辑; 如果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
通过对代码结构逻辑的梳理我们就可以着手改造这段代码了,首先我们先把大的框架写出来,在进行具体业务的逻辑处理
//这里为空则返回空对象 这里表示 如果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 条件参数类型
* @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中的函数。
*
* 函数有返回值
*
* 若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中的函数。若不存在执行默认函数
*
* 函数无返回值 增加默认执行函数 若传入条件皆不符合 则执行默认函数
*
* 若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;
}
/**
* 比较对象类型是否一致 若一致则执行函数
*
* 注意:此方法仅支持 同一个classloader加载两个类使用
*
* 函数无返回值
*
* @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;
}
/**
* 比较对象类型是否一致 若一致则执行函数 若不一致 执行默认函数
*
* 注意:此方法仅支持 同一个classloader加载两个类使用
*
* 函数无返回值 增加默认执行函数 若传入条件皆不符合 则执行默认函数
*
* @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;
}
/**
* 获取当前函数 返回值
*
* 警告: 返回值可能为 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 条件类型泛型
* @param 返回值类型泛型
*/
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));
优点不用多讲…一幕了然呀!!!
敲代码容易,思想不易,转载请标注出处,谢谢诸神…
***江山父老能容我,不使人间造孽钱.***