前端开发 Spring Boot 入门指南:优化枚举重构,完善 Result 失败方法
今日话题
基于 Spring Boot 优化枚举重构,完善 Result 失败方法
作者:云层上的光
时间:2024年7月12日 13时46分14秒
主线任务
优化枚举重构,完善 Result 失败方法依旧在 demo-todo-list 中实现~
一、完善 Result 失败方法
1、改造 common/result 软件包下的 Result 类文件
代码如下:
package com.chuxin.demotodolist.common.result;
import lombok.Data;
import java.io.Serializable;
@Data
public class Result<T> implements Serializable {
// 定义要返回的格式
private int code;
private T data;
private String msg;
// 定义失败的无参构造
public static <T> Result<T> failed() {
Result<T> result = new Result<>();
result.setCode(201);
result.setData(null);
result.setMsg("操作失败");
return result;
}
// 定义失败方法的有参构造
public static <T> Result<T> failed(int code, String msg, T data) {
Result<T> result = new Result<>();
result.setCode(code);
result.setData(data);
result.setMsg(msg);
return result;
}
}
2、改造 controller 中 UserController 类文件,增加 test 方法
代码如下:
package com.chuxin.demotodolist.controller;
import com.chuxin.demotodolist.common.result.Result;
import com.chuxin.demotodolist.entity.User;
import com.chuxin.demotodolist.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
// 测试
@GetMapping("/failedTest")
public Result test() {
// 调用的 Result.failed ,这里调用的是 failed无参构造方法~
return Result.failed();
}
}
3、启动项目
4、浏览器访问:http://localhost:8080/user/failedTest
二、枚举重构
程序代码中痛恨别人写的魔法数字,但是实际项目自己的魔法数字没少写~
1、common/result 软件包下的新建 ResultCode 枚举文件
2、改造 ResultCode 枚举文件,适配接口返回~
代码如下:
package com.chuxin.demotodolist.common.result;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@AllArgsConstructor
@NoArgsConstructor
public enum ResultCode implements Serializable {
SUCCESS(200, "操作成功"), ERROR(201, "操作失败");
private int code;
private String msg;
}
3、枚举类中 code 和 msg 一般不需要 Setter 方法,所以此处不采用 @Data 生成 code 和 msg 的 Setter 方法,手动生成 Getter 方法~
getValue 等价前端数组 find 方法~ 用来拓展获取值~
代码如下:
package com.chuxin.demotodolist.common.result;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@AllArgsConstructor
@NoArgsConstructor
public enum ResultCode implements Serializable {
// 此处 code 200 201 为演示随意写的 不适用真实项目取值~
SUCCESS(200, "操作成功"), ERROR(201, "操作失败");
private int code;
private String msg;
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
// 获取value值,此处的操作和数组 find 一个功能,找到了就返回 没找到就 走 ERROR 这个枚举 不过 js 是 undefined
public static ResultCode getValue(int code) {
for (ResultCode value : values()) {
if (value.getCode().equals(code)) {
return value;
}
}
return ERROR; // 默认系统执行错误
}
}
4、启动项目
5、终端报错:java: 无法取消引用int
6、点击 Ctrl + 鼠标左键 进入到报错文件中
7、由于定义的 code 是 int 类型,而 equals 是校验字符串类型的,此处修改为 == 修复问题~
代码如下:
package com.chuxin.demotodolist.common.result;
import java.io.Serializable;
public enum ResultCode implements Serializable {
// 获取value值,此处的操作和数组 find 一个功能,找到了就返回 没找到就 走 ERROR 这个枚举 不过 js 是 undefined
public static ResultCode getValue(int code) {
for (ResultCode value : values()) {
// 先换成 == 或者把 code 修改为 String 类型
if (value.getCode() == code) {
return value;
}
}
return ERROR; // 默认系统执行错误
}
}
8、改造 common/result 软件包下的 Result 类文件,消除魔法数字
代码如下:
package com.chuxin.demotodolist.common.result;
import lombok.Data;
import java.io.Serializable;
@Data
public class Result<T> implements Serializable {
// 定义要返回的格式
private int code;
private T data;
private String msg;
// 增加 result 方法 简化 failed 的无参构造和有参构造方法的代码量
private static <T> Result<T> result(int code, String msg, T data) {
Result<T> result = new Result<>();
result.setCode(code);
result.setData(data);
result.setMsg(msg);
return result;
}
// 定义失败的无参构造
public static <T> Result<T> failed() {
// 利用枚举消除魔法值
return result(ResultCode.ERROR.getCode(), ResultCode.ERROR.getMsg(), null);
}
}
9、重启项目,浏览器访问:http://localhost:8080/user/failedTest
10、使用枚举消除剩余魔法值
11、重启项目,浏览器访问:http://localhost:8080/user/getList?page=1&pageSize=10
三、接口类 完成 ResultCode 适配
1、真实项目中,希望出现 code 就需要百分百存在 msg,所以我们可以尝试增加接口来进行约束
提示:当然话不能说太满了,根据业务而定~
2、common/result 软件包下的新建 IResultCode 接口文件
3、改造 common/result 软件包下的新建 IResultCode 接口文件
我们不是实体类,不需要描述code msg 这些属性,这里希望约束 ResultCode 中必须要存在
getCode 和 getMsg ,这是因为业务中使用 枚举使用的是 getCode 进行调用~
代码如下:
package com.chuxin.demotodolist.common.result;
public interface IResultCode {
// 描述 code Getter 方法
int getCode();
// 描述 msg Getter 方法
String getMsg();
}
4、改造 common/result 软件包下的新建 ResultCode 枚举文件
代码如下:
package com.chuxin.demotodolist.common.result;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@AllArgsConstructor
@NoArgsConstructor
public enum ResultCode implements IResultCode,Serializable {
// 此处 code 200 201 为演示随意写的 不适用真实项目取值~
SUCCESS(200, "操作成功"), ERROR(201, "操作失败");
private int code;
private String msg;
@Override
public int getCode() {
return code;
}
@Override
public String getMsg() {
return msg;
}
}
5、通过接口进行约束,完成对 getCode 和 getMsg 方法的重写
6、有了接口,在消费层消费 枚举时就更安全,虽然编辑器会自动检测,但是希望养成好习惯编写好的代码~
支线任务
一、Result 其他方法拓展
1、重构 failed 方法,增加 result 方法,简化代码~
2、重启项目,浏览器访问:http://localhost:8080/user/failedTest
3、增强 failed 方法传参业务场景
代码如下:
package com.chuxin.demotodolist.common.result;
import lombok.Data;
import java.io.Serializable;
@Data
public class Result<T> implements Serializable {
// 定义要返回的格式
private int code;
private T data;
private String msg;
// 增加 result 方法 简化 failed 的无参构造和有参构造方法的代码量
private static <T> Result<T> result(int code, String msg, T data) {
Result<T> result = new Result<>();
result.setCode(code);
result.setData(data);
result.setMsg(msg);
return result;
}
// 定义失败方法只传msg
public static <T> Result<T> failed(String msg) {
return result(201, msg, null);
}
// 定义失败方法只传code 和 msg
public static <T> Result<T> failed(int code, String msg) {
return result(code, msg, null);
}
}
4、总结:
其实说白了就是多增加不同的调用方法~
二、枚举重构相关思考
1、枚举在前端历史中一直是 const 的角色在扮演,知道 ts 的出现
const SUCCESS_CODE = 200;
const SRCCESS_MSG = "操作成功"
const ERROR_CODE = 201
const ERROR_MSG = "操作失败"
2、这是由于 const 定义的变量不能修改
3、js 引用类型就不太好约束了
代码如下:
const ERROR = {
CODE: 200,
MSG: "操作成功"
}
// 修改 CODE
ERROR.CODE = 0
// 修改 ERROR
ERROR = {} // 报错: TypeError: Assignment to constant variable.
4、那么 Java 中是怎么实现常量的呢?
5、改造 controller 软件包下的 ResultCode 类
代码如下:
package com.chuxin.demotodolist.controller;
public class ResultCode {
public static final int SUCCESS_CODE = 200;
public static final String SUCCESS_MSG = "操作成功";
public static final int ERROR_CODE = 201;
public static final String ERROR_MSG = "操作失败";
}
6、改造 controller 软件包 UserController 类文件
值得注意的 ResultCode 是当前 controller 软件包下,所以他们的 package 定义的也是相同
所以在上面的 import 引用中 找不到 import 引用 这是应该同一包下可以直接使用
7、启动项目,浏览器访问:http://localhost:8080/user/failedTest
这里 code 等于200 是应该上面代码中传入的是 SUCCESS_CODE
8、不过上面提到 ts 中也实现了枚举
ERROR[ERROR["CODE"] = 0] = "CODE"; 乍一看有点懵,拆开就简单了
首先 ERROR 是一个对象
会先执行 ERROR["CODE"] = 0 然后这个赋值结果会直接返回 0
就会走到 ERROR[0] = "CODE"
访问: ERROR.CODE 返回 0
访问:ERROR[0] 返回 "CODE"
代码如下:
// ts 中 如果不给值 就是默认下标 index 从0开始 (这里不过多介绍)
enum ERROR {
CODE,
MSG
}
9、Java 中实现 ts 的 enum 枚举
单纯作为演示,在 controller 中创建~
10、改造 controller 软件包 ResultEnum 枚举文件
代码如下:
package com.chuxin.demotodolist.controller;
public enum ResultEnum {
SUCCESS_CODE, ERROR_CODE;
}
11、改造 controller 软件包 UserController 类文件
代码如下:
package com.chuxin.demotodolist.controller;
import com.chuxin.demotodolist.common.result.Result;
import com.chuxin.demotodolist.entity.User;
import com.chuxin.demotodolist.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
// 测试
@GetMapping("/failedTest")
public Result test() {
// return Result.failed();
// 这里打印看能取到值么
System.out.println(ResultEnum.SUCCESS_CODE);
// 传参中使用常量
return Result.failed(ResultCode.SUCCESS_CODE, ResultCode.SUCCESS_MSG);
}
}
12、启动项目,浏览器访问 http://localhost:8080/user/failedTest
终端中打印:
13、总结:没有值时
Java 中 没有给值就是值本身,不像前端是对应的数字类型
package com.chuxin.demotodolist.controller;
public enum ResultEnum {
// 获取 SUCCESS_CODE 读到的值是 "SUCCESS_CODE"
SUCCESS_CODE,
ERROR_CODE;
}
14、有值时:
如果有值时看有几个值,如果有两个的话 对应类中就要描述两个属性并且描述类型
还需要描述有参构造和无参构造
15、枚举大总结:
所以枚举只要一个没有参数 就都必须不能有参数 ,如果有一个参数或多个就必须都要有一个或多个参数,
三、枚举相关报错原因
报错是应该没有 ResultCode 有参构造和无参构造的描述方法和声明对应的参数变量,使用 lombok 实现有参构造和无参构造即,并且给出 code 和 msg 描述~
2、解决办法:
使用 lombok 实现有参构造和无参构造 并且给出属性 code msg 描述~
3、枚举描述需要放最上面,反之会报错~
四、枚举参数构造时原理
1、枚举构造的原理是什么呢?
2、为了好理解,我删除了部分代码,只保留到可以理解的程度,先说调用:
代码如下:
result.setCode(ResultCode.SUCCESS.getCode());
这个很好理解:(调方法取值)
ResultCode.SUCCESS.getCode() 的操作其实是子啊调方法取值
- ResultCode.SUCCESS 表示获取枚举 ResultCode 中的 SUCCESS 枚举值
- getCode() 其实是调用 SUCCESS 的 getCode 方法,而不是 ResultCode 的(为什么这么说呢)
@NoArgsConstructor
public enum ResultCode implements IResultCode,Serializable {
// 此处 code 200 201 为演示随意写的 不适用真实项目取值~
SUCCESS(200, "操作成功"), ERROR(201, "操作失败");
private int code;
private String msg;
ResultCode(int code, String msg){
this.code = code;
this.msg = msg;
}
}
3、观点2中为什么说 getCode 是 SUCCESS 的而不是 ResultCode 的呢
代码如下:
package com.chuxin.demotodolist.common.result;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@NoArgsConstructor
public enum ResultCode implements IResultCode,Serializable {
// 此处 code 200 201 为演示随意写的 不适用真实项目取值~
SUCCESS(200, "操作成功"), ERROR(201, "操作失败");
private int code;
private String msg;
ResultCode(int code, String msg){
this.code = code;
this.msg = msg;
}
@Override
public int getCode() {
return code;
}
@Override
public String getMsg() {
return msg;
}
}
这其实是因为:
-
枚举实例创建:
- 在编译阶段,SUCCESS 和 ERROR 枚举实例会被创建。每个实例在创建时都会调用枚举构造器ResultCode(int code, String msg),并将相应的code和msg参数传递给它。这意味着 SUCCESS 实例的code 字段被设置为 200,msg 字段被设置为 "操作成功" ;同理,ERROR 实例的字段被相应设置。
-
字段初始化:
- 在构造器中,通过 this.code = code; 和 this.msg = msg; 语句,枚举实例的字段被初始化。这些字段是枚举实例的一部分,一旦初始化后,它们的值就不会改变。
-
方法调用:
- 当调用 ResultCode.SUCCESS.getCode() 时,实际上是调用了 SUCCESS 枚举实例上的 getCode() 方法。由于 SUCCESS 枚举实例在创建时已经初始化了其 code 字段,因此 getCode() 方法能够返回正确的 code 值,即 200。
-
返回值:
- 最后,getCode() 方法返回枚举实例SUCCESS的code字段的值,即200
代码仓库
往期内容
点击链接查看:www.yuque.com/chuxin-cs/c…
转载自:https://juejin.cn/post/7390631421326983205