java中枚举表示数据状态

1 场景

本文主要讲下java程序中对于状态数据状态代码状态名称如何管理的问题。

对于数据的状态,程序中,经常不会存储状态的名称,而会存储状态对应的代码

每个java后端开发,面对数据库中的一个字段注释:

del_flag  删除状态(0:正常;1:删除;)

都会纠结一个问题,在java代码中,这个字段delFlag对应的01和分别对应的含义正常删除,写在java代码的哪个地方比较合适?

很多人认为这个对于很大的项目来说无非是个很小的事,写在哪里,无所谓,都不会影响我们的项目。讲道理,这个无法反驳。

这篇文章,让我们一起剖析下这件微不足道的小事。

在这里,我们为了方便,定义状态代码状态名称的类型均为字符串

2 步骤

2.1 直接使用

最简单的用法,就是直接在程序中使用状态的代码,如下。

//(1)设置删除标记为正常
record.setDelFlag("0");
......
//(2)判断删除标志
if("0".equals(record.getDelFlag())){
    return "正常";
}
2.1.1 优点

项目初期管理简单,不用花费时间进行代码封装。

2.1.2 缺点

(1)项目中出现大量手写状态代码状态名称。代码检查工具会提示魔法值,如项目中对代码检查要求高,此部分代码无法合并主干代码。

(2)如果状态代码对应的状态名称发生了变动,全局更改起来,将是十分致命的。

(3)研发人员水平不一致,手动在代码里写状态代码和状态名称,十分容易写错

2.2 静态常量

部分项目,会定义一个java类,将状态码定义为其中的静态常量。其中定义了项目中用到的所有状态代码

public class StateContext{
	/**
	 * 正常
	 */
	public static final String NORMAL="0";
	
	/**
	 * 删除
	 */
	public static final String DEL="1";
    //其他状态代码
    ......
}

静态常亮的使用方式如下:

//(1)设置删除标记为正常
record.setDelFlag(StateContext.NORMAL);
......
//(2)判断删除标志
if(StateContext.NORMAL.equals(record.getDelFlag())){
    return "正常";
}
2.2.1 优点

状态代码,可以直接通过类的静态变量使用(状态名称,也可以通过类的静态变量定义)。

2.2.2 缺点

(1)需要在类中大量定义静态变量,使用时,需十分明确使用的变量的名字,勿使用成别的变量。

(2)一般只通过静态变量定义状态代码状态名称也可这么定义。使用时,对应关系容易对应不上。

2.3 枚举

每种状态信息,定义专门的枚举类。

/**
 * 删除标志枚举
 */
public enum DelFlagEnum{
    
    NORMAL("0","正常"),
    DEL("1","删除");
    
    private DelFlagEnum(String code,String name){
        this.code=code;
        this.name=name;
    }
    
    /**
     * 代码
     */
    private String code;
    
    /**
     * 名称
     */
    private String name;
    
    public String getCode() {
        return code;
    }
    
    public String getName() {
        return name;
    }
    
}
2.3.1 优点

(1)每种状态定义一个专门的枚举类,枚举类中的定义十分明确

(2)可通过枚举属性准确对应的状态代码和状态名称。

(3)定义的枚举是有属性的,可以在枚举中定义专门的特殊方法,应对特殊的业务。

2.3.2 缺点

枚举主动在代码中使用,是没有问题的。如下:

DelFlagEnum.NORMAL.getCode();

但是状态代码一般都会存储在数据库中,如果通过数据库中获取的状态代码获取枚举对象,基础的枚举信息,无法实现此功能。

故可以在枚举中,定义专门的方法,通过状态代码获取枚举或通过状态名称获取枚举。如下

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.EnumUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.List;

/**
 * 删除标志枚举
 */
public enum DelFlagEnum{
    
    NORMAL("0","正常"),
    DEL("1","删除");
    
    private DelFlagEnum(String code,String name){
        this.code=code;
        this.name=name;
    }
    
    /**
     * 枚举集合
     */
    private static final List<DelFlagEnum> ENUM_LIST= EnumUtils.getEnumList(DelFlagEnum.class);
    
    /**
     * 通过代码获取枚举
     * @param code 枚举代码
     * @return 枚举
     */
    public static DelFlagEnum getEnumByCode(String code){
        if(StringUtils.isNotEmpty(code) && CollectionUtils.isNotEmpty(ENUM_LIST)){
            for(DelFlagEnum delFlagEnum:ENUM_LIST){
                if(code.equals(delFlagEnum.getCode())){
                    return delFlagEnum;
                }
            }
        }
        return null;
    }
    
    /**
     * 通过名称获取枚举
     * @param name 枚举名称
     * @return 枚举
     */
    public static DelFlagEnum getEnumByName(String name){
        if(StringUtils.isNotEmpty(name) && CollectionUtils.isNotEmpty(ENUM_LIST)){
            for(DelFlagEnum delFlagEnum:ENUM_LIST){
                if(name.equals(delFlagEnum.getName())){
                    return delFlagEnum;
                }
            }
        }
        return null;
    }
    
    /**
     * 代码
     */
    private String code;
    
    /**
     * 名称
     */
    private String name;
    
    public String getCode() {
        return code;
    }
    
    public String getName() {
        return name;
    }
}


但是,如果每个枚举都是这样写。代码量大大增加,极易出现拷贝导致的低级错误。因此需对此代码进行封装,减少开发人员的工作量。

2.4 枚举+命名空间

此种方式,是对2.3中枚举定义状态值进行了代码封装

这是作者当前找到的最优的方式,建议使用。

2.4.1 枚举基础接口

定义基础枚举接口。接口中定义枚举中必有的两个方法(获取代码、获取名称)

public interface BaseEnum {
    /**
     * 获取代码
     * @return
     */
    String getCode();
    
    /**
     * 获取名称
     * @return
     */
    String getName();
}

2.4.2 枚举实现类

每种状态对应专门的枚举类,每个枚举类均需实现接口BaseEnum(javac编译器,枚举无法继承,所以成员变量、接口方法,还需要手动写)。

枚举类中仍然可以定义专门的方法,来执行特殊业务。

public enum DelFlagEnum implements BaseEnum{
    
    NORMAL("0","正常"),
    DEL("1","删除");
    
    /**
     * 代码
     */
    private String code;
    
    /**
     * 名称
     */
    private String name;
    
    private DelFlagEnum(String code, String name){
        this.code=code;
        this.name=name;
    }
    
    @Override
    public String getCode() {
        return this.code;
    }
    
    @Override
    public String getName() {
        return this.name;
    }
}

2.4.3 枚举命名空间

可以通过此命名空间实现如下功能:

(1)通过代码获取枚举(时间复杂度O(1))

(2)通过名称获取枚举(时间复杂度O(n))

上述两个方法均线程安全,且均为懒加载,即用到方法时才去初始化对应的枚举类缓存。

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 枚举命名空间
 */
public class EnumContext {
    
    /**
     * 枚举集合映射
     */
    private static final Map<String, Map<String, BaseEnum>> ENUM_MAP = new ConcurrentHashMap<>();
    
    /**
     * 初始化枚举map
     */
    private synchronized static <T extends BaseEnum> void initEnumMap(Class<T> enumClass) {
        if (enumClass != null && enumClass.isEnum()) {
            String key = enumClass.getName();
            if (!ENUM_MAP.containsKey(key)) {
                Map<String, BaseEnum> map = new ConcurrentHashMap<>();
                BaseEnum[] baseEnums = enumClass.getEnumConstants();
                if (baseEnums != null && baseEnums.length > 0) {
                    for (final BaseEnum e : baseEnums) {
                        map.put(e.getCode(), e);
                    }
                }
                ENUM_MAP.put(key, map);
            }
        }
    }
    
    /**
     * 通过代码获取枚举(时间复杂度O(1))
     *
     * @param code      代码
     * @param enumClass 枚举类
     * @return 枚举
     */
    public static <T extends BaseEnum> T getEnumByCode(String code, Class<T> enumClass) {
        if (StringUtils.isNotEmpty(code) && enumClass != null) {
            String key = enumClass.getName();
            if (!ENUM_MAP.containsKey(key)) {
                //如果不存在=>初始化
                initEnumMap(enumClass);
            }
            //如果已存在=>判断
            Map<String, BaseEnum> map = ENUM_MAP.get(key);
            if (MapUtils.isNotEmpty(map)) {
                return (T) map.get(code);
            }
        }
        return null;
    }
    
    /**
     * 通过名称获取枚举(时间复杂度O(n))
     *
     * @param name      代码
     * @param enumClass 枚举类
     * @return 代码
     */
    public static <T extends BaseEnum> T getEnumCodeByName(String name, Class<T> enumClass) {
        if (StringUtils.isNotEmpty(name) && enumClass != null) {
            String key = enumClass.getName();
            if (!ENUM_MAP.containsKey(key)) {
                //如果不存在=>初始化
                initEnumMap(enumClass);
            }
            //如果已存在=>判断
            Map<String, BaseEnum> map = ENUM_MAP.get(key);
            if (MapUtils.isNotEmpty(map)) {
                for (Map.Entry<String, BaseEnum> entry : map.entrySet()) {
                    if (entry.getValue() != null && name.equals(entry.getValue().getName())) {
                        return (T) entry.getValue();
                    }
                }
            }
        }
        return null;
    }
}

2.4.3 使用
//直接使用枚举
System.out.println(DelFlagEnum.NORMAL.getCode() + "  " + DelFlagEnum.NORMAL.getCode());

//通过代码获取枚举
DelFlagEnum normalEnum = EnumContext.getEnumByCode("0", DelFlagEnum.class);
if (normalEnum != null) {
    System.out.println(normalEnum.getCode() + "  " + normalEnum.getName());
}

//通过名称获取枚举
DelFlagEnum delEnum = EnumContext.getEnumCodeByName("删除", DelFlagEnum.class);
if (delEnum != null) {
    System.out.println(delEnum.getCode() + "  " + delEnum.getName());
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值