编码规约之使用Enum枚举类替代魔法值

目录

一、魔法值概念

1.警示案例1

2.警示案例2

二、重构方案

1.静态常量(不要使用)

2.枚举类

三、总结


一、魔法值概念

常量在代码中具有穿透性,使用甚广。如果没有一个恰当的命名,就会给代码阅读带来沉重的负担,甚至影响对主干逻辑的理解。首当其冲的问题就是到处使用魔法值。

魔法值即"共识层面"上的常量,直接以具体的数值或者字符出现在代码中。这些不知所云的魔法值极大地影响了代码的可读性和可维护性

1.警示案例1

    public void getOnlinePackageCourse(Long packageId,Long userId){
        if(packageId == 3){
            logger.error("线下课程,无法在线观看");
            return;
        }
    }
    //其他逻辑处理
    PackageCourse online = packageService.getByTeacherId(userId);
    if(online.getPackageId() == 2){
        logger.error("未审核课程");
        return;
    }

以上代码中,信手拈来的2和3分别表示未审核课程和线下课程,仅仅是两个数字,似乎很容易记忆。但事实上,除了2和3两种状态外,还有1、4、5分别代表新建、审核未通过、审核通过。在团队规模较小时,口口相传,倒也勉强能够记住这五个数字的含义,早期还有零星的注释,驾轻就熟的情况下,连注释也省了。现实是残酷的,团队迅速扩大后,课程状态个数也在逐步增加,新来的开发工程师在上线新功能模块时,把"审核通过"和"未审核课程"对应的数字搞反了,使得课程展示错误,导致用户大量投诉。

2.警示案例2

    String key = "Id#taobao_" + tradeId;
    cache.put(key,value);

 上述代码是保存信息到缓存中的方法,即使用魔法值组装Key。这就导致各个调用方导出复制和粘贴字符串Id#taobao_,这样似乎很合理。但某一天,某个粗心的程序员把Id#taobao_复制成了Id#taobao,少了下划线。这个错误在测试过程中,并不容易发现,因为没有命中缓存,会自动访问数据库。但在大促时,数据库压力急剧上升,进而发现缓存全部失效,导致连接占满,查询变慢。"小洞不补,大洞吃亏",再次说明魔法值害人害已。

随着应用变得越来越复杂,这些魔法值几乎成了整个后台服务代码中的梦魇。团队架构师终于下定决心进行系统重构。

二、重构方案

1.静态常量(不要使用)

一是,静态全局常量不安全,开发者传入任意类型的对应类型值。二是,没有命名空间,对常量命名有所要求。详情见另一篇文章Java之枚举类

2.枚举类

下述代码把课程类型分成了三种:录播课程,直播课程,线下课程。枚举类型几乎是固定不变的全局变量,使用频率高、范围广,所以枚举常量都需要添加清晰的注释,比如业务相关信息或注意事项等。

	public enum CourseTypeEnum{
		/**
		 * 允许官方和讲师创建和运营
		 */
		VIDEO_COURSE(1,"录播课程"),

		/**
		 * 只允许官方和讲师创建和运营,初始化必须设置合理的报名人数上线
		 */
		LIVE_COURSE(2,"直播课程"),

		/**
		 * 只允许官方和讲师创建和运营
		 */
		OFFLINE_COURSE(3,"线下课程");

		private int seq;
		private String desc;
		CourseTypeEnum(int seq,String desc){
			this.seq = seq;
			this.desc = desc;
		}

		public int getSeq(){
			return seq;
		}

		public String getDesc(){
			return desc;
		}
	}

再把课程状态分为新课程、未审核课程、审核通过、审核未通过、已删除五种状态。考虑到后续课程状态还会再追加,并且状态没有扩展信息,所以不能用实例化的抽象类的全局常量(枚举类)来表示课程状态(枚举类适合所有常量已经确定,后期几乎固定不变,这样才符合枚举类的宗旨)

    public abstract class BaseCourseState{
    	public static final int NEW_COURSE = 1;  
    	public static final int UNAUTHED_COURSE = 2;  
    	public static final int PASSED_COURSE = 3;  
    	public static final int NO_PASSED_COURSE = 4;  
    	public static final int DELETED_COURSE = 5;  
    }

 使用重构后的常量修改原有魔法值,对比下代码的可读性

    public void getOnlinePackageCourse(Long packageId,Long userId){
        if(packageId == CourseTypeEnum.OFFLINE_COURSE.getSeq()){
            logger.error("线下课程,无法在线观看");
            return;
        }
    }
    //其他逻辑处理
    PackageCourse online = packageService.getByTeacherId(userId);
    if(online.getPackageId() == BaseCourseState.UNAUTHED_COURSE){
        logger.error("未审核课程");
        return;
    }

三、总结

即使类内常量和局部常量当前只使用一次,也需要赋予一个有意义的名称,目的有两个:第一,望文知义,方便理解;第二,后期多次使用时能保证值出同源。因此,无论如何都不允许任何魔法值直接出现在代码中避免魔法值随意使用导致取值不一致,特别是对于字符串常量来说,应该避免没有预先定义,就直接使用魔法值。 

某些公认的字面常量是不需要预先定义的,如for(int i=;..)这里的0是可以直接使用的。true和false也可以直接使用,但是如果具备了特殊的含义,就必须定义出有意义的常量名称,比如在TreeMap源码中,表示红黑树节点颜色的true和false就被定义成为类内常量,以方便理解

    	public static final boolean RED = false;  
    	public static final boolean BLACK= true;  

 

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值