7 种让 if / else 变得更加优雅的方式,你 pick 了吗?
共 1484字,需浏览 3分钟
·
2020-12-01 10:50
前言
判断可以说是我们程序中必不可少的逻辑处理方式,最简单的就是 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 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 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.bootgroupId>
<artifactId>spring-boot-starter-validationartifactId>
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 fieldErrors = bindingResult.getFieldErrors();
return fieldErrors.get(0).getDefaultMessage();
}
return dataDto + " is ok!!!";
}
}
我们可以来看看校验的结果对象里面都有哪些信息
当接口多了,每个接口如果都来个 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格式 |
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之道公众号
好文章,我在看❤️