Java枚举最佳实践

Java枚举最佳实践

枚举是 Java 开发中必不可少的一部分,常用来定义单例工具类,常量,状态字典等,本文主要介绍枚举当做字典的最佳实践

一、问题背景

一直在做前后端分离开发的同学应该都遇到过这样一个问题,比如有个字段性别,有男,女,未知,前端是下拉选传递男 1,女 2,未知 3 过来,后端存入数据库的时候都是存 0,1,2 枚举值,这样的话如果后端查询相关性别数据的时候只能查到 0,1,2 这样的数据,并不能返回对应的描述字段,如果前端也要具体的描述字段该怎么办呢?有两种方案

  1. 前端写死字典表,后端循环重新组装数据给前端
  2. 提供前端字典表接口,后端只传递 code 值,具体的 name 描述字段由前端转换

之前公司都是采用第一种方案,数据都由后端提供,开发迅速,免得跟前端扯皮,但是使用一方案有个问题:如果我想增加一种枚举,那岂不是前端后端的字典表都要改

其实我个人觉得第二种方案更合理,但是作为单体项目,我又不想创建字表相关的表,想直接使用枚举类当做字典表返回给前端,岂不美哉,所以就有了以下的想法:

把程序中需要提供给前端的枚举类存入到缓存,给前端提供字典查询接口,这样即使新增枚举也只要改后端就可以了

二、最佳实践

1. 枚举抽象类

public interface BaseCodeEnum<T{

    getCode();

    String getName();

}
  • 限制枚举字段约束,为了之后的枚举工具类做准备
/**
 * @author: sunhhw
 * @date: 2023/12/8 12:25
 * @description: 字典枚举值标记
 */

public interface DictEnumAware<Textends BaseCodeEnum<T{

}
  • Aware 结尾标记,标识实现该接口的枚举是需要当做字典返回给前端的
public enum ArticleStatusEnum implements DictEnumAware<Integer> {

    PUBLISH(1"发布"),
    DRAFTS(0"草稿箱"),
    ;

    private final Integer code;
    private final String name;

    ArticleStatusEnum(Integer code, String name) {
        this.code = code;
        this.name = name;
    }

    @Override
    public Integer getCode() {
        return this.code;
    }

    @Override
    public String getName() {
        return this.name;
    }
}
  • 如果只是程序内部使用,则实现 BaseCodeEnum 类即可

2. 扫描枚举类

public enum EnumUtils {

    /**
     * 枚举标记
     */

    X;

       /**
     * 根据枚举class获取该枚举的所有code,name列表
     */

    public <T extends DictEnumAware<N>, N> DictEnumDTO getEnumList(Class<T> enumClass) {
        DictEnumDTO dictEnumDTO = new DictEnumDTO();
        List<DictDTO> dictDTOList = new ArrayList<>();
        for (T typeEnum : enumClass.getEnumConstants()) {
            DictDTO dictDTO = new DictDTO();
            N code = typeEnum.getCode();
            String name = typeEnum.getName();
            dictDTO.setCode(code.toString());
            dictDTO.setName(name);
            dictDTOList.add(dictDTO);
        }
        dictEnumDTO.setDictDTOList(dictDTOList);
        dictEnumDTO.setDictName(enumClass.getSimpleName());
        return dictEnumDTO;
    }


    public List<DictEnumDTO> registry(String basePackage) {
        List<DictEnumDTO> dictEnumDTOList = new ArrayList<>();
        try {
            PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
            MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(resourcePatternResolver);
            String packageSearchPath = "classpath*:" + basePackage.replace('.''/') + "/**/*.class";
            Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);

            for (Resource resource : resources) {
                MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
                ClassMetadata classMetadata = metadataReader.getClassMetadata();
                if (classMetadata.isInterface() || classMetadata.isAbstract()) {
                    // 接口或抽象类,不处理
                    continue;
                }
                String[] interfaceNames = classMetadata.getInterfaceNames();
                for (String interfaceName : interfaceNames) {
                    if (interfaceName.equals(DictEnumAware.class.getName())) {
                        String className = classMetadata.getClassName();
                        Class<? extends DictEnumAware<Object>> clazz = (Class<? extends DictEnumAware<Object>>) Class.forName(className);
                        DictEnumDTO dictEnumDTO = getEnumList(clazz);
                        dictEnumDTOList.add(dictEnumDTO);
                        break;
                    }
                }
            }
        } catch (Exception e) {
            throw new ServiceException("字典类型初始化失败", e);
        }
        return dictEnumDTOList;
    }
}
  • 使用枚举做单例工具类,其中部分 DTO 类自行补充
  • 使用PathMatchingResourcePatternResolverMetadataReaderFactory扫描指定包路径下的类,并筛选出该类是否实现DictEnumAware接口,找出对应的枚举类
  • 然后获取该枚举类的 codename 值,封装到 DTO 对象中

3.存入缓存

@Test
public void test_01() {
    String packageName = "com.blog";
    List<DictEnumDTO> dictEnumDTOList = EnumUtils.X.registry(packageName);
    for (DictEnumDTO dictEnumDTO : dictEnumDTOList) {
        // 存入缓存
    }
}

4.提供接口

需要提供两个接口:

  • 一个是根据枚举名称查询所有枚举列表
  • 一个是根据枚举名称和枚举 code 查询枚举 name

原文始发于微信公众号(一零贰肆):Java枚举最佳实践

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/250567.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!