当前位置:网站首页>枚举通用接口&枚举使用规范

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

2022-07-06 20:18:00 InfoQ

  • 数据表里字段值为有穷序列的字段,对应到程序里特定的枚举。字段数据类型尽量用varchar取代int(或tinyint)。毋庸置疑,字母组合总比0、1、2、3这样的数字易于识别。
  • 数据表字段如果有对应的枚举,则,在字段注释上要标明枚举类名,方便程序溯源。
  • 枚举一般有两部分,一个是枚举项值,一个是枚举描述。那么,这两个属性怎么命名呢? code和desc?还是value和desc?还是key和value?本文重点针对这个问题阐述。

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

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

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

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

深谙其理。深有体会。

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

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

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

@Getter
@AllArgsConstructor
public 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
@AllArgsConstructor
public 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;
 }
}


@AllArgsConstructor
public 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
@AllArgsConstructor
public 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
@AllArgsConstructor
public enum LevelEnum implements EnumAbility<String> {

 FIRST(&quot;FIRST&quot;, &quot;一级&quot;),
 SECOND(&quot;SECOND&quot;, &quot;二级&quot;),
 THIRD(&quot;THIRD&quot;, &quot;三级&quot;);


 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
@AllArgsConstructor
public enum OrderStatusEnum implements EnumAbility<String> {

 INIT(&quot;INIT&quot;, &quot;初始&quot;),
 ACCOUNTING(&quot;ACCOUNTING&quot;, &quot;记账中&quot;),
 SUCCESS(&quot;SUCCESS&quot;, &quot;付款成功&quot;),
 FAILED(&quot;FAILED&quot;, &quot;付款失败&quot;);


 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);
 }
}

@AllArgsConstructor
public enum ProductEnum implements EnumAbility<String> {
 BOSSKG(&quot;BOSS开工&quot;),
 HUICHUXING(&quot;惠出行&quot;),
 SICHEBANGONG(&quot;私车办公&quot;),
 YOUFU(&quot;优付&quot;),
 UNKNOWN(&quot;未知&quot;),
 ;
 
 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
@AllArgsConstructor
public enum SeasonEnum implements EnumAbility<Integer> {
 SPRING(1, &quot;春&quot;),
 SUMMER(2, &quot;夏&quot;),
 AUTUMN(3, &quot;秋&quot;),
 WINTER(4, &quot;冬&quot;);
 private Integer code;
 private String description;

 public static SeasonEnum getBeanByCode(Integer code) {
 if (null == code) return null;
 return (SeasonEnum) EnumAbilityUtil.getEnumByCode(SeasonEnum.class, code);
 }
}

原网站

版权声明
本文为[InfoQ]所创,转载请带上原文链接,感谢
https://xie.infoq.cn/article/2c0d113217bd62c8e9571f2df