7 种让 if / else 变得更加优雅的方式,你 pick 了吗?

前言

判断可以说是我们程序中必不可少的逻辑处理方式,最简单的就是 if / else 语句,可很多人就因为判断必不可少而让我们的程序因为 if / else 变得臃肿和冗长,而随着时间推移,业务的增加,导致代码逻辑复杂,维护性差。

接下来我将介绍 7 种方式让我们的代码变得更加优雅。

1. 简化 else

场景:符合/不符合条件的操作无交集。

优化前:

if (id == null) {
    // TODO 新增记录
} else {
    // TODO 修改记录
}

优化后:

if (id == null) {
    // TODO 新增记录
    return;
}
// TODO 修改记录

2. 三目运算符

场景:符合/不符合条件操作后的返回值不同。

优化前:

String name;
if (user == null) {
    name = "无名氏";
} else {
    name = user.getName();
}

优化后:

String name = user == null ? "无名氏" : user.getName();

3. Java 8 Optional 类

Optional 可以把它想象成一个容器,值存在则 isPresent()true,再调用 get() 会返回该对象。

场景:需要对值进行 null 判断。

优化前:

String name;
if (user == null) {
    name = "无名氏";
} else {
    name = user.getName();
}

优化后:

// ofNullable()里可以放 null 值;of()里放 null 会抛出空指针异常 NullPointerException
Optional<User> optionalUser = Optional.ofNullable(user);
String name = optionalUser.isPresent() ? optionalUser.get().getName() : "无名氏";

Optional 的另一种使用方式

优化前:

private void printName(String name) {
    if (name == null) {
        name = "无名氏";
    }
    log.info("姓名:{}", name);
}

优化后:

private void printName(String name) {
    // 如果值存在,正常返回里面的值,否则返回默认值(无名氏)
    Optional<String> optional = Optional.ofNullable(name);
    log.info("姓名:{}", optional.orElse("无名氏"));
}

优点:可以很好地避免烦人的 NPE。

4. 卫语句

如果代码中使用的 if / else 超过了三层,建议替换,避免后续代码维护困难。

场景:多种条件判断,都为 true 才能执行。

优化前:

if (id == null) {
    log.error("id 为空");
    return;
} else if (name == null) {
    log.error("姓名为空");
    return;
} else if (age < 0) {
    log.error("年龄需要大于0");
    return;
}        
// TODO 正常执行

优化后:

if (id == null) {
    log.error("id 为空");
    return;
}
if (name == null) {
    log.error("姓名为空");
    return;
}
if (age < 0) {
    log.error("年龄需要大于0");
    return;
}
// TODO 正常执行

优点:可读性高;当有条件不需要后可以直接删除相应“开关”,更加直观。

5. switch

如果代码中使用的 if else 超过了三层,建议替换,避免后续代码维护困难。

场景:不同条件下执行的操作或返回的值不一样

优化前:

int a = 2;
int b = 5;
if ("PLUS".equals(opt)) {
    return a + b;
} else if ("MINUS".equals(opt)){
    return a - b;
} else if ("MULTIPLY".equals(opt)) {
    return a * b;
} else {
    return a / b;
}

优化后:

private int calculate(String opt) {
    int a = 2;
    int b = 5;
    switch (opt) {
        case "plus":
            return a + b;
        case "minus":
            return a - b;
        case "multiply":
            return a * b;
        default:
            return a / b;
    }
}

优点:可读性高;当有条件不需要后可以直接删除相应开关,更加直观。

6. hibernate-validator

Controller 层接口入参时不妨试试它,别再 if / else 一层层判断了

该校验包在 Spring Boot 2.3.0 之前的 spring-boot-starter-web 依赖中已经自带了,可以直接使用。

Spring Boot 2.3.0 之后,校验包已经被独立出来了,需要额外再引入依赖。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

场景:接口入参需要进行大量的参数校验判断时

优化前:

优化后:

@Data
public class DataDto {
    @Min(value = 1, message = "id不能小于1")
    @NotNull(message = "id不能为空")
    private Integer id;

    @NotBlank(message = "姓名不能为空")
    private String name;

    @Email(message = "email无效")
    @NotNull(message = "email不能为空")
    private String email;

    @NotNull(message = "手机号不能为空")
    @Pattern(regexp = "^(13[0-9]|14[579]|15[0-3,5-9]|16[6]|17[0135678]|18[0-9]|19[89])\\d{8}$", message = "手机号无效")
    private String phone;
}

使用 @Valid 进行校验,校验后的结果放入后面的 bindingResult 对象中。

BindingResult 必须紧跟着需要被校验的参数对象。

@RestController
public class DemoController {

    @PostMapping("demo")
    public String testParameters(@RequestBody @Valid DataDto dataDto, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            List<FieldError> fieldErrors = bindingResult.getFieldErrors();
            return fieldErrors.get(0).getDefaultMessage();
        }
        return dataDto + " is ok!!!";
    }

}
name 传空字符串

我们可以来看看校验的结果对象里面都有哪些信息

email 传错误格式
正常传参

当接口多了,每个接口如果都来个 BindingResult 也是个体力活儿,这时候就可以考虑全局异常了。

@ControllerAdvice
public class RequestBadExceptionHandler {

    @ResponseBody
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public String methodArgumentException(MethodArgumentNotValidException e) {
        // 拿到ObjectError错误信息对象
        ObjectError objectError = e.getBindingResult().getAllErrors().get(0);
        // 返回对象里的错误提示信息
        return "参数错误:" + objectError.getDefaultMessage();
    }

}

去除 BindingResult

@PostMapping("demo")
public String testParameters(@RequestBody @Valid DataDto dataDto) {
    return dataDto + " is ok!!!";
}
传入无效手机号
正常传参

怎么样,这样的入参校验是不是比 if / else 要更加优雅?

Hibernate Validator 官网说明:https://docs.jboss.org/hibernate/validator/6.0/reference/en-US/html_single/

以下列出一些常用的校验注解

注解校验
@NotNull不为null
@NotEmpty不为 null 并且不为空字符串
@NotBlank不为 null 并且包含至少一个非空白字符
@Min最小值
@Max最大值
@Digits设定最大整数位数和最大小数位数
@DecimalMin大于等于给定的值
@DecimalMax小于等于给定的值
@Past必须是过去的时间
@PastOrPresent必须是过去的时间,包含现在
@Future必须是将来的时间
@FutureOrPresent当前或将来时间
@PositiveOrZero正数或0
@Email校验是否符合Email格式

7. 策略模式

场景:不同条件下需要执行不同的操作

优化前:

public Integer count(Integer a, Integer b, String opt) {
    if ("PLUS".equals(opt)) {
        return a + b;
    } else if ("MINUS".equals(opt)){
        return a - b;
    } else if ("MULTIPLY".equals(opt)) {
        return a * b;
    } else {
        return a / b;
    }
}

优化后:

采用枚举实现策略模式

public interface ArithmeticOperation {
    
    /**
     * 计算
     *
     * @param a 待计算值
     * @param b 待计算值
     * @return 计算结果
     */
    int calculate(int a, int b);

}
public enum ArithmeticEnum implements ArithmeticOperation {

    /**
     * 加
     */
    PLUS {
        @Override
        public int calculate(int a, int b) {
            return a + b;
        }
    },
    /**
     * 减
     */
    MINUS {
        @Override
        public int calculate(int a, int b) {
            return a - b;
        }
    },
    /**
     * 乘
     */
    MULTIPLY {
        @Override
        public int calculate(int a, int b) {
            return a * b;
        }
    },
    /**
     * 除
     */
    DIVIDE {
        @Override
        public int calculate(int a, int b) {
            return a / b;
        }
    };

}
public Integer count(Integer a, Integer b, String opt) {
    ArithmeticEnum arithmeticEnum = ArithmeticEnum.valueOf(opt);
    return arithmeticEnum.calculate(a, b);
}

优点:这样我们的代码就很好地做到了横向扩展,不用再写一堆的 if / else 进行判断,后期维护也变得更容易了。

用到策略模式的场景还有很多,比如订单来源不同需要做不同操作,使用优惠券打折分折扣券和抵扣券……

最后

这里面的7种你平常都用到了哪些?我觉得还是挺好掌握的,快去看看自己代码有哪里可以优化的迅速改起来吧!!!

有道无术,术可成;有术无道,止于术

欢迎大家关注Java之道公众号

好文章,我在看❤️

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 博客之星2020 设计师:CY__0809 返回首页