项目中如何优雅的使用枚举类型

原文链接:赵侠客

前言

枚举类型在开发中是很常见的,有非常多的应用场景,如状态管理、类型分类、权限控制、配置管理、错误码管理、日志级别等。正确合理的使用枚举可以给我们带来非常多的好处:

  1. 增强代码可读性:枚举可以使得代码更加清晰、易于理解。它们提供了一种方式来组织和表示相关的常量值,使得代码更易于阅读和维护。
  2. 类型安全性:枚举类型能够限制变量的值,只能取枚举类型中定义的常量之一,从而避免了错误的赋值。这有助于减少代码中的错误,并提高代码的稳定性。
  3. 更好的维护性:枚举类型可以在编译时进行类型检查,这有助于更早地发现和修复问题。此外,由于枚举类型中的常量值是预定义的,因此可以减少对常量值的修改,从而简化代码的维护。
  4. 更好的性能:枚举类型的值是在编译时确定的,因此在运行时访问枚举类型的值会更快。此外,由于枚举类型中的常量值是唯一的,因此可以直接使用“==”进行两个值之间的对比,这有助于提高性能。
  5. 更好的组织性:枚举类型可以帮助我们将相关的值组织在一起,使代码更加整洁。通过将相关的常量值组合在一起,可以使代码更加易于理解和维护。
  6. 可扩展性:枚举类型可以轻松地扩展或更新,而不会对其他部分的代码造成影响。这有助于保持代码的灵活性和可扩展性。
  7. 便于测试:枚举类型可以方便地进行测试,因为它们具有有限且确定的值域。这使得测试人员可以更容易地覆盖所有可能的场景,并确保代码的正确性。

虽然枚举有诸多的好处,但是使用枚举也给我们带来了一些困扰:

  1. 前后端数据格式转换:前端主要给用户展示数据,不能直接显示枚举值,需要前端将枚举转成用户可读的数据显示
  2. 数据库的存储:代码中的枚举类型无法直接存储数据库,一般转成数值类型,这样还可以减少存储空间
  3. 代码中大量类型转换:查询时需要数值类型转成枚举类型,保存时又需要将枚举类型转成数值类型

针对枚举存在的问题,本文介绍一种枚举从数据库–>后端代码–>前端代码–>页面和从页面–>前端代码–>后端代码–>数据库的自动转换方案,大大方便前后端使用枚举类型。

image.png

自动转换目标

我们以用户状态为例,用户有两种状态:禁用和启用

  • 前端页面:前端页面显示用户状态时用“禁用、启用”;
  • 前端代码:前端代码里处理用户状态时用:“ENABLE、DISABLE”或者用“0、1”;
  • 后端代码:后端代码使用StatusEnum枚举类;
  • 数据库:数据库存储用户状态时禁用存1、启用存0。
    image.png
    我们的目标是让枚举在各个环境流转时全自动转换。

代码与数据库自动转换

第一步创建统一的枚举基类BaseEnum

public interface BaseEnum {int getCode();String getName();String getEnumName();static <T extends BaseEnum> T getInstance(Class<T> clazz, String value) {T[] constants = clazz.getEnumConstants();for (T t : constants) {if(StrUtil.isNumeric(value)){if (t.getCode() == Integer.parseInt(value)) {return t;}}else {if (t.getEnumName().equals(value)) {return t;}}}return null;}
}

第二步创建用户状态类StatusEnum实现BaseEnum接口

public enum StatusEnum implements BaseEnum {ENABLE(0,"启用"),DISABLE(1,"禁用");@EnumValueprivate int code;private String name;StatusEnum(int code, String name) {this.code = code;this.name=name;}@Overridepublic int getCode() {return code;}@Overridepublic String getName() {return name;}@Overridepublic String getEnumName() {return this.name();}
}

BaseEnum主要有三个方法

  1. getCode()获取枚举的数值如“0、1”;
  2. getName()获取枚举显示值如“禁用、启用” ;
  3. getEnumName()获取枚举的枚举值如“ENABLE、DISABLE”.

如果使用MybatisPlus, 可以使用@EnumValue注解很方便的帮我们解决数据库与实体对象中枚举类型的相互转换,如果只使用的Mybatis可以自定义TypeHandler来解决数据库到JAVA枚举对象的自动转换。

第三步创建用户类User用户状态使用StatusEnum

@Data
@TableName("user")
public class User {private Long id;private String userName;private StatusEnum status;
}

前后端相互转换

当前端查询用户时,我们希望将枚举的三个属性都返回给前端,前端页面显示时取status.name代码中使用status.enum或者status.code

{"id": 3581209395268,"userName": "test2@8531.cn","status": {"name": "禁用","enum": "DISABLE","code": 1}
}

为了达到将枚举序列化成一个json对象,我们需要自定义序列化器和反序列化器,以下以SpringBoot自带的Jackson为例:

public class BaseEnumSerializer extends StdSerializer<BaseEnum> {public BaseEnumSerializer() {this(null);}public BaseEnumSerializer(Class<BaseEnum> t) {super(t);}@Overridepublic void serialize(BaseEnum value, JsonGenerator gen, SerializerProvider provider) throws IOException {gen.writeStartObject();gen.writeStringField("name",value.getName());gen.writeStringField("enum",value.getEnumName());gen.writeNumberField("code",value.getCode());gen.writeEndObject();;}
}
public class BaseEnumDeserializer<T extends BaseEnum> extends StdDeserializer<T> {private Class<T> type;public BaseEnumDeserializer() {this(null);}public BaseEnumDeserializer(Class<T> vc) {super(vc);type = vc;}@Overridepublic T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {return BaseEnum.getInstance(type, p.getText());}
}

自定义Jackson序列化器与反序列化器只能解决数据类型为application/json格式的请求,当请求类型为application/x-www-form-urlencoded我们还需要自定义Spring消息转换器

'public class NumBaseEnumConverterFactory implements ConverterFactory<Number, BaseEnum> {@Overridepublic <T extends BaseEnum> Converter<Number, T> getConverter(Class<T> aClass) {return new NumberToEnumConverter<>(aClass);}private final class NumberToEnumConverter<T extends BaseEnum> implements Converter<Number, T> {private Class<T> enumType;public NumberToEnumConverter(Class<T> enumType) {this.enumType = enumType;}@Overridepublic T convert(Number s) {return BaseEnum.getInstance(enumType,s.toString());}}
}public class StrBaseEnumConverterFactory implements ConverterFactory<String, BaseEnum> {@Overridepublic <T extends BaseEnum> Converter<String, T> getConverter(Class<T> aClass) {return new StringToEnumConverter<>(aClass);}private final class StringToEnumConverter<T extends BaseEnum> implements Converter<String, T> {private Class<T> enumType;public StringToEnumConverter(Class<T> enumType) {this.enumType = enumType;}@Overridepublic T convert(String s) {return BaseEnum.getInstance(enumType,s);}}
}

以上两个消息转换器可以在数据格式以表单形式提交时将数值类型(0、1)和枚举值类型(ENABLE、DISABLE)转成枚举类型。

将自定义好的数据转换器注入到Spring中,这样就完成所有枚举自动转换。

@Configuration
public class WebConfig implements WebMvcConfigurer {@Beanpublic ObjectMapper objectMapper() {ObjectMapper mapper = new ObjectMapper();SimpleModule module = new SimpleModule();module.addSerializer(BaseEnum.class, new BaseEnumSerializer());module.addDeserializer(BaseEnum.class, new BaseEnumDeserializer<>());mapper.registerModule(module);mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);return mapper;}@Overridepublic void addFormatters(FormatterRegistry registry) {registry.addConverterFactory(new StrBaseEnumConverterFactory());registry.addConverterFactory(new NumBaseEnumConverterFactory());}
}

查询用户

GET http://localhost:90/user/3581209395268
返回:
{"id": 3581209395268,"userName": "test2@8531.cn","status": {"name": "启用","enum": "ENABLE","code": 0}
}

application/json格式传参

POST http://localhost:90/user
Content-Type: application/json{"id": 3581209395268,"status": "DISABLE"
}###
POST http://localhost:90/user
Content-Type: application/json{"id": 3581209395268,"status": "0"
}

application/x-www-form-urlencoded格式传参

PUT http://localhost:90/user
Content-Type: application/x-www-form-urlencodedid=3581209395268&status=ENABLE
###
PUT http://localhost:90/user
Content-Type: application/x-www-form-urlencodedid=3581209395268&status=1###
PUT http://localhost:90/user/3581209395268?status=ENABLE
Content-Type: application/x-www-form-urlencoded###
PUT http://localhost:90/user/3581209395268?status=1
Content-Type: application/x-www-form-urlencoded

@PathVariable格式传参

PUT http://localhost:90/user/3581209395268/ENABLE
Content-Type: application/x-www-form-urlencoded###
PUT http://localhost:90/user/3581209395268/1
Content-Type: application/x-www-form-urlencoded

对应JAVA代码:

@RestController
public class UserController {@Resourceprivate UserMapper userMapper;@GetMapping("/user/{id}")public User getById(@PathVariable Long id) {return userMapper.selectById(id);}@PostMapping("/user")public User upadteById(@RequestBody User user) {userMapper.updateById(user);return user;}@PutMapping("/user")public User updateUser(User user) {userMapper.updateById(user);return user;}@PutMapping("/user/{id}/{status}")public User updateStatus(@PathVariable Long id,@PathVariable StatusEnum status) {User user=userMapper.selectById(id);user.setStatus(status);userMapper.updateById(user);return user;}@PutMapping("/user/{id}")public User updateUserStatus(@PathVariable Long id,@RequestParam StatusEnum status) {User user=userMapper.selectById(id);user.setStatus(status);userMapper.updateById(user);return user;}
}

这样很方便的解决了枚举在各个环节的自动转换问题,其它枚举只要实现BaseEnum接口就能实现全自动转换,前后端用起来也方便了不少。

总结

本文主要介绍了项目中使用枚举的优缺点,并针对缺点给出了解决方案,解决了枚举在项目中频繁转换的问题,当然解决的还不是非常完美,比如返回给前端的枚举格式是:{"enum":"DISABLE","code":1}
但是保存时传此数据结构,后端却无法正确的转成枚举,我们可以创建StatusEnumDeserializer,将子json对象转成对应枚举就好了,但是范型的写法目前还不知道怎么写,不可能增加一个枚举写一个反序列化器,有知道的可以回复一下,相互学习。

public class StatusEnumDeserializer   extends JsonDeserializer<StatusEnum>  {@Overridepublic StatusEnum  deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {JsonNode node= p.getCodec().readTree(p);if(node.isObject()){String name= node.get("enum").toString();return BaseEnum.getInstance(StatusEnum.class, name);}else {return BaseEnum.getInstance(StatusEnum.class, node.textValue());}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/720044.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pandas.DataFrame新增列、dropna()方法-丢弃含空值的行、列;inf的处理技巧

在Dataframe中新添加一列 直接指明列名&#xff0c;然后赋值就可 import pandas as pddata pd.DataFrame(columns[a,b], data[[1,2],[3,4]]) data >>> dataa b 0 1 2 1 3 4 添加一列’c‘&#xff0c;赋值为空白值。打印出来 data[c] data >>>…

1255942-05-2,DBCO-C6-Amine,可以用于构建分子结构和生物活性分子

您好&#xff0c;欢迎来到新研之家 文章关键词&#xff1a;1255942-05-2&#xff0c;DBCO C6 NH2&#xff0c;DBCO-C6-Amine&#xff0c;二苯并环辛炔-C6-氨基 一、基本信息 【产品简介】&#xff1a;DBCO-C6-NH2 is a multifunctional molecule with excellent chemical re…

【王道操作系统】ch1计算机系统概述-05操作系统引导

文章目录 【王道操作系统】ch1计算机系统概述-05操作系统引导01 什么是操作系统引导02 磁盘里边有哪些相关数据&#xff08;1&#xff09;主引导记录&#xff08;MBR&#xff09;&#xff08;2&#xff09;活动分区&#xff08;一般是C盘&#xff09; 03 操作系统引导的过程 【…

如何创建MinIO存储服务公网地址实现固定TCP域名异地远程访问——“cpolar内网穿透”

文章目录 前言1. 创建Buckets和Access Keys2. Linux 安装Cpolar3. 创建连接MinIO服务公网地址4. 远程调用MinIO服务小结5. 固定连接TCP公网地址6. 固定地址连接测试 前言 MinIO是一款高性能、分布式的对象存储系统&#xff0c;它可以100%的运行在标准硬件上&#xff0c;即X86等…

每日一题 — 盛水最多的容器

11. 盛最多水的容器 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 因为体积是长度乘高&#xff0c;所以运用双指针&#xff0c;一个在最左&#xff0c;一个在最右&#xff0c;每次都记录体积 V &#xff0c;然后比较左边的长度和右边的长度&#xff0c;左边的长度…

【代码随想录算法训练营Day35】435.无重叠区间;763.划分字母区间;56.合并区间

文章目录 ❇️Day 36 第八章 贪心算法 part05✴️今日任务❇️435. 无重叠区间自己的思路自己的代码&#xff08;✅通过81.59%&#xff09;随想录思路随想录代码 ❇️763.划分字母区间自己的思路自己的代码&#xff08;✅通过55.30%&#xff09;随想录思路随想录代码 ❇️56. 合…

长/短 链接/轮询 和websocket

短连接和长连接 短连接&#xff1a; http协议底层基于socket的tcp协议&#xff0c;每次通信都会新建一个TCP连接&#xff0c;即每次请求和响应过程都经历”三次握手-四次挥手“优点&#xff1a;方便管理缺点&#xff1a;频繁的建立和销毁连接占用资源 长连接&#xff1a; 客…

Javaweb之SpringBootWeb案例之自动配置案例的自定义starter分析的详细解析

3.2.4.1 自定义starter分析 前面我们解析了SpringBoot中自动配置的原理&#xff0c;下面我们就通过一个自定义starter案例来加深大家对于自动配置原理的理解。首先介绍一下自定义starter的业务场景&#xff0c;再来分析一下具体的操作步骤。 所谓starter指的就是SpringBoot当…

java 多线程分段下载

java 多线程分段下载 介绍 使用java 多线程下载&#xff0c;当前只是一个demo,还没进行对比测试&#xff0c;目前看速度确实要快一些 实现和简单就是启用多个现成分段下载&#xff0c;最后再组合在一起 完整代码 原本是下载tomcat10的&#xff0c;但是我本地jdk不符&#…

机器人工具箱学习(二)

一、机械臂及运动学 1.1 机械臂构成 机械臂多采用关节式机械结构&#xff0c;一般具有6个自由度&#xff0c;其中3个用来确定末端执行器的位置&#xff0c;另外3个则用来确定末端执行装置的方向(姿态)。   如图所示&#xff0c;一个机械臂是由一组可做相对运动的关节连接的连…

Arduino应用开发——使用GUI-Guider制作LVGL UI并导入ESP32运行

Arduino应用开发——使用GUI-Guider制作LVGL UI并导入ESP32运行 目录 Arduino应用开发——使用GUI-Guider制作LVGL UI并导入ESP32运行前言1 使用GUI-Guider设计UI1.1 创建工程1.2 设计UI 2 ESP工程导入UI2.1 移植LVGL2.2 移植UI文件2.3 调用UI文件2.4 烧录测试 结束语 前言 GU…

二维码门楼牌管理系统技术服务:楼牌编设规则详解

文章目录 前言一、院落内新增楼栋的楼牌编制二、围合式或分片式建筑群的楼牌编设三、围合单位建筑内部的楼牌编制四、独栋单位建筑的楼牌编制五、无名称院落的楼牌编制六、同一裙楼上多栋楼房的楼牌编制 前言 随着城市建设的快速发展&#xff0c;门楼牌管理成为城市管理的重要…

山西电力市场日前价格预测【2024-02-27】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2024-02-27&#xff09;山西电力市场全天平均日前电价为332.28元/MWh。其中&#xff0c;最高日前电价为544.59元/MWh&#xff0c;预计出现在19:00。最低日前电价为211.48元/MWh&#xff0c;预计…

如何在Linux使用Docker部署Redis并结合内网穿透实现公网远程连接本地数据库

文章目录 前言1. 安装Docker步骤2. 使用docker拉取redis镜像3. 启动redis容器4. 本地连接测试4.1 安装redis图形化界面工具4.2 使用RDM连接测试 5. 公网远程访问本地redis5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定TCP地址远程访问 正文开始前给大家推荐个网站…

还在手动Word转PPT?快来试试这些一键生成工具!

在我们日常的工作和学习中&#xff0c;将Word转化成PPT的需求时常出现&#xff0c;尤其是当我们需要进行演讲或者报告时。这不仅能使我们的演讲更具视觉冲击力&#xff0c;也有助于我们更好地传达信息。 那么&#xff0c;如何才能轻松地将Word转换成PPT呢&#xff1f;下面将为…

商家转账到零钱功能申请方法

商家转账到零钱是什么&#xff1f; 【商家转账到零钱】可以说是【企业付款到零钱】的升级版&#xff0c;商家转账到零钱可以为商户提供同时向多个用户微信零钱转账的能力&#xff0c;支持分销返佣、佣金报酬、企业报销、企业补贴、服务款项、采购货款等自动向用户转账的场景。…

Intel 芯片 Mac 如何重新安装系统

使用可引导安装器重新安装&#xff08;可用于安装非最新的 Mac OS&#xff0c;系统降级&#xff0c;需要清除所有数据&#xff0c;过程确保连接上网络&#xff0c;虽然这种方式不会下载 Mac OS&#xff0c;但是需要下载固件等信息&#xff09; 插入制作好的可引导安装器&#x…

leetcode 热题 100_找到字符串中所有字母异位词

题解一&#xff1a; 滑动窗口&#xff1a;类似于字符串匹配&#xff0c;但匹配异位词需要包含相同的字母及个数&#xff0c;可以分别用两个数组存储字符串s滑动窗口和字符串p的字母及个数&#xff0c;再用Array.equals()进行比对。对于s.length()<p.length()的情况需要特判。…

【python】python用户管理系统[简易版](源码+报告)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

怎么异地共享文件?

不同地点的团队成员之间共享文件是现代企业中常见的需求之一。随着分布式团队的不断增加&#xff0c;找到一种安全、高效的方式来实现异地共享文件变得尤为重要。本文将介绍一种名为【天联】的工具&#xff0c;它可以帮助团队成员在异地互相共享文件。 【天联】是一种专门为异地…