魔法值规范
1.介绍
在代码审查(Code Review)中,最令人血压升高的场景之一,莫过于看到类似的代码:
if (user.getStatus() == 3) {
}
这个3到底是什么?是已冻结?已删除?还是VIP等级?除了写代码的人(甚至一周后的他自己),没人知道。这就是臭名昭著的魔法值(Magic Number)
2.为什么要消除魔法值
消除魔法值可以提升代码的可读性和可维护性
3.解决方案
- 常量类
- 枚举
- 配置文件
4.常量类
介绍
这是最直观的解决方案。通常表现为一个Constants类,里面塞满了public static final
示例
public class AppConstants {
public static final int MAX_RETRY_COUNT = 3;
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public static final double PI = 3.1415926;
}
// 使用
if (retryCount < AppConstants.MAX_RETRY_COUNT) {
// ...
}
优点
- 零认知门槛:任何语言的开发者都能看懂
- 性能极高:编译期常量,直接替换,无运行时开销
- 通用性强:适用于数学常量,技术标准等不依赖上下文的数值
缺点
- 弱类型约束:int只是int。如果方法接收int status,你传MAX_RETRY_COUNT进去编译器也不会报错,但逻辑全错了
- 缺乏内聚性:容易演变成垃圾桶模式(God Class),所有不相关的常量(数据库表名,Redis Key,错误码)都塞在一个文件里,导致类极其臃肿
- 无行为能力:常量只是值,无法携带逻辑
适用场景
- 数学或物理常数
- 技术层面的固定限制
- 不需要分组,不需要逻辑扩展的全局单一值
5.枚举值
介绍
携带逻辑的富对象,当你的魔法值代表的是一组有限的,确定的集合时,枚举才是王道
示例
public enum OrderStatus {
CREATED(0,"待支付"),
PAID(1,"已支付"),
SHIPPED(2,"已发货");
private final int code;
private final String desc;
OrderStatus(int code,String desc) {
this.code = code;
this.desc = desc;
}
// 可以添加业务逻辑
public boolean isTerminalState() {
return this == SHIPPED;
}
}
// 使用
if (order.getStatus() == OrderStatus.PAID) {
// ...
}
优点
- 强类型安全:方法签名如果是process(OrderStatus status),你绝对传不进去一个整数,彻底杜绝参数传递错误
- 高内聚:数据与行为绑定在一起。你可以直接调用OrderStatus.PAID.getDesc()用于前端展示
- 易于维护:增加一种状态只需加一行代码,且可以通过values()遍历
缺点
- 内存开销略大:相比于int常量,Enum是对象,会占用更多的堆内存(但在现代服务器上通常可忽略)
- 序列化坑:在RPC或JSON序列化时,如果通过ordinal()(索引)传输,一旦枚举顺序改变,业务就会崩盘(建议永远使用name()或自定义code)
适用场景
- 状态机:订单状态,审批流节点
- 类型:用户类型(VIP/普通),支付方式(微信/支付宝)
- 错误码定义:将错误码和错误信息绑定
- 策略模式:利用枚举实现简单的策略分发
6.配置文件
介绍
有些值虽然也是常量,但它们的值可能会随着环境(开发、测试、生产)变化,或者需要由运维人员动态调整
示例
app:
upload:
max-file-size: 10MB
allowed-types: jpg,png
payment:
timeout-seconds: 30
优点
- 环境隔离:Dev环境超时设为100秒方便调试,Prod环境设为5秒快速失败。代码不用改,只改配置
- 动态调整:配合Nacos、Apollo等配置中心,可以在不重启服务的情况下修改阈值(如降级开关、限流阈值)
- 解耦:将业务逻辑与基础设施参数分离
缺点
- 非编译期检查:配置写错了(比如把30写成30s),可能要等到运行报错才发现
- 读取成本:需要依赖注入或读取工具类,不如Class.CONSTANT那么随手可得
适用场景
- 基础设施连接信息:IP,端口,URL,账号密码
- 业务开关/阈值:功能特性开关(Feature Flag)、超时时间、线程池大小、限流速率
- 经常变动的业务规则:比如新用户注册赠送积分数,如果运营经常改,就不要写死在代码里