写点什么

枚举通用接口 & 枚举使用规范

作者:GREENSOUL
  • 2022 年 7 月 06 日
  • 本文字数:3556 字

    阅读完需:约 12 分钟

枚举通用接口&枚举使用规范
  1. 数据表里字段值为有穷序列的字段,对应到程序里特定的枚举。字段数据类型尽量用 varchar 取代 int(或 tinyint)。毋庸置疑,字母组合总比 0、1、2、3 这样的数字易于识别。

  2. 数据表字段如果有对应的枚举,则,在字段注释上要标明枚举类名,方便程序溯源。

  3. 枚举一般有两部分,一个是枚举项值,一个是枚举描述。那么,这两个属性怎么命名呢? code 和 desc?还是 value 和 desc?还是 key 和 value?本文重点针对这个问题阐述。


为什么要用枚举? 有穷序列的字段用 int 或 tinyint 不是挺好吗?


答案很简单:“企业应用中,我们的代码首先是写给人看的,其次才是给机器执行”,许多编码规范都在强调这一点。


既然是写给人看,那么,可读、易读、可理解、易理解往往显得相当重要!


我曾经认识的一位架构师强调过:对于企业应用系统,当前的硬件资源和技术条件已经很给力了,通常情况下,我们大可不必为了追求性能而将类型、状态等字段定义成数字类型;而编写易理解、可维护的系统,已经越发显得重要了。将这些字段定义成字符串类型,用字符串来描述字段值,就是易理解的一个重要体现。


深谙其理。深有体会。


而且,我也一直在这么践行。


关于枚举的好处,我曾连篇赘述->www.cnblogs.com/buguge/tag/枚举/。本文继续。


随着项目的不断开发迭代,大家在项目里定义的枚举越来越多。这些枚举类的结构很相同,不同之处仅仅是各枚举项。另外,在对枚举的使用方面也五花八门,毕竟每个人对技术的理解和认知甚至工作态度是不同的,而且,程序运行过程中偶尔还会有 NPE 找上门来。


@Getter@AllArgsConstructorpublic enum LevelEnum {  FIRST("FIRST", "一级"),  SECOND("SECOND", "二级"),  THIRD("THIRD", "三级");

private String code; private String value;
public static LevelEnum getBeanByCode(String code) { LevelEnum[] statusEnums = LevelEnum.values(); for (LevelEnum v : statusEnums) { if (v.getCode().equals(code)) { return v; } } return null; }}
复制代码


@Getter@AllArgsConstructorpublic enum OrderStatusEnum {
INIT("INIT", "初始"), ACCOUNTING("ACCOUNTING", "记账中"), SUCCESS("SUCCESS", "付款成功"), FAILED("FAILED", "付款失败");

private String key; private String description;
public static OrderStatusEnum getBeanByCode(String code) { OrderStatusEnum[] values = OrderStatusEnum.values(); for (OrderStatusEnum v : values) { if (v.getKey().equals(code)) { return v; } } return null; }}
复制代码


@AllArgsConstructorpublic enum ProductEnum {  BOSSKG("BOSS开工"),  HUICHUXING("惠出行"),  SICHEBANGONG("私车办公"),  YOUFU("优付"),  UNKNOWN("未知"),  ;
private String description;
private String getCode(){ return this.toString(); }
public String getDescription() { return description; }
public static ProductEnum getBean(String value) { ProductEnum[] values = ProductEnum.values(); for(ProductEnum temp : values){ if(temp.getCode().equals(value)){ return temp; } } return ProductEnum.UNKNOWN; }}
复制代码


@Getter@AllArgsConstructorpublic enum SeasonEnum {  SPRING(1, "春"),  SUMMER(2, "夏"),  AUTUMN(3, "秋"),  WINTER(4, "冬");    private int code;  private String description;                                                  	public static SeasonEnum getBeanByCode(Integer code) {      if (null == code) return null;      SeasonEnum[] values = SeasonEnum.values();      for (SeasonEnum temp : values) {          if (temp.getCode() == code) {              return temp;          }      }      return null;  }}
复制代码


秉承着老司机的职业素养,我站在团队开发规范的角度上思考,能否为这些枚举定义统一的接口,这样,大家在使用的时候就更统一了。下面 interface 是我和组内一个优秀的小伙伴的成果,它界定了枚举的通用操作:

/** * 如若枚举名称和实际值不同,请务必重写getKey方法 * 枚举定义规范:枚举名切记大写,描述尽量清晰,不要出现错误拼写,请自觉仔细检查 * 例如: * MONDAY("星期一"), * TUESDAY("星期二") * * @author shaozhengmao * @create 2021-06-21 10:18 上午 */public interface EnumAbility<T> {
/** * 返回枚举实际值 * @return */ T getCode();
/** * 返回枚举描述 * * @return 枚举说明 */ String getDescription();
/** * 对比当前枚举对象和传入的枚举值是否一致(String类型会忽略大小写) * 当前枚举项是否匹配远端传入的值(比如:数据库的字段值、rpc传入的参数值) * * @param enumCode 枚举code * @return 是否匹配 */ default boolean codeEquals(T enumCode) { if (enumCode == null) return false; if (enumCode instanceof String) { return ((String) enumCode).equalsIgnoreCase((String) getCode()); } else { return Objects.equals(this.getCode(), enumCode); } }
/** * 对比两个枚举项是否完全相同(==) * * @param anotherEnum 枚举 * @return 是否相同 */ default boolean equals(EnumAbility<T> anotherEnum) { return this == anotherEnum; }}
复制代码


同时,还定义了一个工具方法,用来根据 code 来获取对应的枚举项:


这样,上面示例中的枚举将简化为:

@Getter@AllArgsConstructorpublic enum LevelEnum implements EnumAbility<String> {
FIRST("FIRST", "一级"), SECOND("SECOND", "二级"), THIRD("THIRD", "三级");

private String code; private String value;
@Override public String getDescription() { return value; }
/** * 2021-12-18 23:00 zhanggz:本方法存在歧义,请使用{@link #getDescription()} * @return */ @Deprecated public String getValue() { return value; }
public static LevelEnum getBeanByCode(String code) { return (LevelEnum) EnumAbilityUtil.getEnumByCode(LevelEnum.class, code); }}
复制代码


@Getter@AllArgsConstructorpublic enum OrderStatusEnum implements EnumAbility<String> {
INIT("INIT", "初始"), ACCOUNTING("ACCOUNTING", "记账中"), SUCCESS("SUCCESS", "付款成功"), FAILED("FAILED", "付款失败");

private String key; private String description;
@Override public String getCode() { return key; } /** * 2021-12-18 23:00 zhanggz:本方法存在歧义,请使用{@link #getCode()} * @return */ @Deprecated public String getKey() { return key; }
public static OrderStatusEnum getBeanByCode(String code) { return (OrderStatusEnum) EnumAbilityUtil.getEnumByCode(OrderStatusEnum.class, code); }}
复制代码


@AllArgsConstructorpublic enum ProductEnum implements EnumAbility<String> {    BOSSKG("BOSS开工"),    HUICHUXING("惠出行"),    SICHEBANGONG("私车办公"),    YOUFU("优付"),    UNKNOWN("未知"),    ;        private String description;
@Override private String getCode(){ return this.toString(); }
@Override public String getDescription() { return description; } public static ProductEnum getBean(String code) { return (ProductEnum) EnumAbilityUtil.getEnumByCode(ProductEnum.class, code); }}
复制代码


@Getter@AllArgsConstructorpublic enum SeasonEnum implements EnumAbility<Integer> {    SPRING(1, "春"),    SUMMER(2, "夏"),    AUTUMN(3, "秋"),    WINTER(4, "冬");    private Integer code;    private String description;
public static SeasonEnum getBeanByCode(Integer code) { if (null == code) return null; return (SeasonEnum) EnumAbilityUtil.getEnumByCode(SeasonEnum.class, code); }}
复制代码


发布于: 刚刚阅读数: 3
用户头像

GREENSOUL

关注

还未添加个人签名 2020.08.17 加入

还未添加个人简介

评论

发布
暂无评论
枚举通用接口&枚举使用规范_枚举_GREENSOUL_InfoQ写作社区