How to gracefully eliminate if.. else.. Make the code look good? Come on, guys
How to gracefully remove if from your code… else; How to gracefully avoid the hated NPE, this may be a programmer’s lifelong pursuit, today to bring you a case, a bloated fat,P into a rich and handsome process. J8’s Optional container is used. Of course, there must be some friends who use it better than me, or understand it better than ME. If I write something wrong, please point it out. We can learn together and make progress together.
if(I am tall rich handsome){system.out ("Objects line up.")}else{
System.out("Writing code in frustration and pain.")}Copy the code
Don’t go on about it. First show you a code, if you do not dizzy, I blow two bottles of erguotou…..
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 instanceofList) { 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)) {
assertcouponDetails ! =null;
for (CouponDetail couponDetail : couponDetails) {
if (ObjectUtil.isEmpty(couponDetail)) {
platformDiscount = 0;
}
if(ObjectUtil.isNotEmpty(couponDetail) && ObjectUtil.isNotEmpty(couponDetail.getCouponPrice())) { platformDiscount += Double.parseDouble(couponDetail.getCouponPrice()); }}returnString.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)) {
assertcouponDetails ! =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()); }}returnString.valueOf(platformDiscount); }}}return String.valueOf(platformDiscount);
}
returnString.valueOf(platformDiscount); }}Copy the code
Look at this code, if you see this code, if there is no nausea physiological reaction, then congratulations to you, Lanxiang excavator professional is the best way to change your life. Next, let’s modify the code to make it look good… (This uncommented code)
- The business card
(1), first of all, we first look at the big framework here, is composed of two two logical blocks, empty logic; The default value is returned if source is empty, and the calculated value is returned if source is not empty. If source is not empty, check whether the source type is List. If not, return the default value. ③ When source is list and not empty, determine the generics in list (business constraints,list of the same generic, there is no multiple types exist at the same time). (4) When source is not empty, two business logic judgments are performed, namely the judgments of the following two classes, which follow different conversion logic. Otherwise, the default value is returned
com.jd.open.api.sdk.domain.order.OrderQueryJsfService.response.enGet.CouponDetail
com.jd.open.api.sdk.domain.order.OrderQueryJsfService.response.search.CouponDetail
Copy the code
- The code to build
By combing the code structure logic we can begin to transform this code, first of all, we first write out the big framework, in the specific business logic processing
If source is not empty, the execution continues. If source is empty, the default value is returned
Optional.ofNullable(source)
// If source is of type list, this continues; if not, the default value is returned
.filter(fSource -> fSource instanceof List)
// Perform the calculation if source is of type list
.map(mSource -> {
// Do the calculation or next step logic here
})
// The default value is returned
.orElse(String.valueOf(platformDiscount));
Copy the code
From the above code, we can see that the framework logic of this logical code has been built. The following is the technical analysis of the code :(look at the blackboard)
Source ==ofNullable()== Optional instantiates in three ways:
Create empty Optional object empty(); Create a non-empty Optional object,of(); The object in the container must not be empty, otherwise an exception will be thrown; Create a non-empty Optional object,ofNullable();
2.2. Use filter() to check whether the source object is a List; Filter () returns optional. If true, an optional container with an object is returned, otherwise empty.
2.3 use Map () to calculate the internal details of the business logic; I won’t go into the details of the map() method here, but there are plenty of resources on the web for you to look at; Here are some important points :(look at the blackboard)
(1) Map () receives the same value (type) of the object in the container returned by the previous logic. The map() method actively creates Optional containers for objects passed in. This is different from the flatMap() method. The map() method returns an empty Optional object if the object is empty. The map() method returns an empty Optional object if the object is not empty
If orElse() returns a default value, the Optional method returns the default value. If orElse() returns a default value, the Optional method returns the calculated value. The orElse() method is always executed, regardless of whether the object in your current Optional container is empty. If it is null and if it is not null, the orElseGet() method is used.
Next we fill in the detail logic:
If source is not empty, the execution continues. If source is empty, the default value is returned
Optional.ofNullable(source)
// If source is of type list, this continues; if not, the default value is returned
.filter(fSource -> fSource instanceof List)
// Perform the calculation if source is of type list
.map(mSource -> {
// If source is of type list, use? To represent theList<? > objectList = (List<? >) source;// Fetch the first element of the list. There is no way to tell if the element in the list is empty.
// If the value of the attribute in the element is empty, you also need to use the Optional container to avoid NPE.
The findFirst() method returns an Optional container, so we'll use Map directly
return objectList
.stream()
.findFirst()
// The map firstSource is the first element in the list.
.map(firstSource ->
// It is important to note that since objects in Optional cannot be passed at the same level, this is explained in the technical analysis above;
FunctionBuilder
// Create an IF function instance
.returnIf(new HashMap<Object, FunctionReturn<String>>())
// Add function conditions and execute functions
.addReturn(CouponDetail.class, () -> ifSearchCouponDetail(source))
// Add function conditions and execute functions
.addReturn(com.jd.open.api.sdk.domain.order
.OrderQueryJsfService.response.enGet
.CouponDetail.class, () -> ifEnGetCouPonDetail(source))
// The actual entry object
.doIfInstance(firstSource)
/ / the default value
.defaultValue(null)
).orElse(null);
})
// The default value is returned
.orElse(String.valueOf(platformDiscount));
Copy the code
Isolate the specific business code into methods.
IfEnGetCouPonDetail (), ifSearchCouponDetail(), and ifSearchCouponDetail()
@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);
}
Copy the code
@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);
}
Copy the code
Analysis of the above code technical points:
From the above code. Clear logic, simple structure, readable; FunctionBuilder (Optional) : FunctionBuilder (Optional) : FunctionBuilder (Optional) : FunctionBuilder (Optional) : FunctionBuilder (Optional)
(Optional) If the object in the Optional container is empty (Optional), the value of the object in the Optional container is null (Optional). For example, if you use two map() methods, the first map contains the User object, and the second map() contains the Optional object in the first map(), the returned object cannot be the same User object.
Here I also post part of the source package
FunctionBuilder class
/ * * *@Author: yangjiahui
* @DescriptionTODO builds the if function creation utility class *@Date: "2021/03/22 * /
public class FunctionBuilder {
/** * creates the if function ** with no return value@paramMap map < condition, execute function > *@param<K> Conditional parameter type *@returnIf function instance *@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) {
returnIfFunctionReturn.<K, T>builder().buildReturnIf(returnMap).build(); }}Copy the code
IfFunctionReturn class
Here is an if function with a return value
/ * * *@Author: yangjiahui
* @Description: TODO an instance of the if function with a return value *@Date: 2021/03/05 5:43 下午
*/
public class IfFunctionReturn<K.T> {
private Map<K, FunctionReturn<T>> mapReturn;
/** * This value is used if the value is not null. * Otherwise false. If null, no value */ exists
private T result;
public void setMap(Map<K, FunctionReturn<T>> mapReturn) {
this.mapReturn = mapReturn;
}
public IfFunctionReturn(a) {}/** * add condition to return value function **@paramKey The condition for validation (key) *@paramFunctionReturn The method to execute *@return this.
*/
public IfFunctionReturn<K, T> addReturn(K key, FunctionReturn<T> functionReturn) {
this.mapReturn.put(key, functionReturn);
return this;
}
/** * Batch add condition has return value function **@paramKey The condition for validation (key) *@paramFunctionReturn The method to execute *@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;
}
/** * determines whether key exists, and if so, executes the function in value. * <p> * The function returns a value * <p> * Overriding the equal and hashCode methods if the key is an object type * * The key value must be the same as the map key value * *@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;
}
/** * determines whether key exists, and if so, executes the function in value. * <p> * The equal and hashCode methods must be overridden if the key is an object type * * The key must be the same as the key in map * *@paramKey The key need to verify conditional value */
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> * This method only supports the same classloader to load two classes@paramKey The key need to verify conditional value */
public IfFunctionReturn<K, T> doIfInstance(@NotNull K key) {
mapReturn.forEach((setKey, value) -> {
if (setKey.equals(key.getClass())) {
this.result = value.invokeReturn(); }});return this;
}
* <p> * Note: this method only supports the same classloader to load two classes. Use * <p> * function with no return value to add a default function * *@paramKey The key need to verify conditional value */
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;
}
Warning: The return value may be null **@returnReturns the object */
public T get(a) {
if (result == null) {
throw new NoSuchElementException("Return value object is empty!");
}
return result;
}
public T defaultValue(T other) {
returnresult ! =null ? result : other;
}
/** * Automatically refresh the map to prevent data conflicts */
public IfFunctionReturn<K, T> refresh(a) {
this.mapReturn.clear();
return this;
}
/** * Create a bridge instance **@param<K> Conditional type generic *@param<T> Return value type generic */
public static <K, T> ResIfFunctionReturn<K, T> builder(a) {
return new ResIfFunctionReturn<>();
}
public static class ResIfFunctionReturn<K.T> {
private Map<K, FunctionReturn<T>> mapReturn;
private ResIfFunctionReturn(a) {}public ResIfFunctionReturn<K, T> buildReturnIf(Map<K, FunctionReturn<T>> mapReturn) {
this.mapReturn = mapReturn;
return this;
}
public IfFunctionReturn<K, T> build(a) {
IfFunctionReturn<K, T> functionReturn = new IfFunctionReturn<>();
functionReturn.setMap(mapReturn);
returnfunctionReturn; }}}Copy the code
FunctionReturn Function interface
Encapsulation of interfaces to perform business logic
/ * * *@Author: yangjiahui
* @DescriptionTODO executes the function * with a return value@Date: 2020/12/22 4:20pm */
@FunctionalInterface
public interface FunctionReturn<T> {
/** * a function that returns a value *@return* /
T invokeReturn(a);
}
Copy the code
In fact, the if function encapsulation is very simple, but there is no need to repeat.
Third, analyse
Let’s take a look at the pre-optimized and post-optimized code;
Before optimization :11 if decision logic (code with business cut)
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){}
}
returnString.valueOf(platformDiscount); }}if (object instanceofcom... enGet.CouponDetail) {if (ObjectUtil.isNotEmpty(couponDetails)) {
if (ObjectUtil.isEmpty(couponDetail)) {}
if (ObjectUtil.isNotEmpty(couponDetail)
}
returnString.valueOf(platformDiscount); }}}return String.valueOf(platformDiscount);
}
return String.valueOf(platformDiscount);
}
Copy the code
After the optimization:
Optional.ofNullable(source)
.filter(fSource -> fSource instanceofList) .map(mSource -> { List<? > objectList = (List<? >) source;return objectList
.stream()
.findFirst()
.map(firstSource ->
FunctionBuilder
.returnIf(newHashMap<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));
Copy the code
Advantages need not say more…. Act clear ah!!!!!!
Easy to knock code, not easy to think, reprint please mark the source, thank the gods…
Jiangshan father can let me, do not make the world of evil money