Java 枚举的 3 个实战场景:从状态码到策略模式
枚举不只是"定义常量",用好了能让代码干净一半。
适合人群 :刚学完 Enum 语法、想知道"实际项目中怎么用"的同学。
阅读时间:约 8 分钟。
目录
- [场景 1:状态 / 类型定义(最常用)](#场景 1:状态 / 类型定义(最常用))
- [场景 2:策略模式(替换大量 if/else)](#场景 2:策略模式(替换大量 if/else))
- [场景 3:统一返回码(后端接口必备)](#场景 3:统一返回码(后端接口必备))
- 完整可运行代码
- 总结
场景 1:状态 / 类型定义(最常用)
问题:魔法数字与魔法字符串
java
// 烂代码示例
if (order.getStatus() == 1) {
// 待支付
} else if (order.getStatus() == 2) {
// 已支付
}
// 1 和 2 是什么意思?新同事一脸懵,数据库里也是裸数字
枚举重构
文件:OrderStatus.java
java
public enum OrderStatus {
PENDING_PAYMENT("待支付", 1),
PAID("已支付", 2),
SHIPPED("已发货", 3),
COMPLETED("已完成", 4),
CANCELLED("已取消", 5);
private final String desc;
private final int code;
OrderStatus(String desc, int code) {
this.desc = desc;
this.code = code;
}
public String getDesc() { return desc; }
public int getCode() { return code; }
// 根据 code 反查枚举(数据库里存的是 code)
public static OrderStatus of(int code) {
for (OrderStatus status : values()) {
if (status.code == code) return status;
}
throw new IllegalArgumentException("未知状态码: " + code);
}
}
文件:OrderStatusDemo.java
java
public class OrderStatusDemo {
public static void main(String[] args) {
// 1. 基本使用
OrderStatus status = OrderStatus.PAID;
System.out.println("当前状态: " + status.getDesc());
System.out.println("状态码: " + status.getCode());
// 2. 遍历所有状态(适合展示枚举的规范性)
System.out.println("\n=== 所有订单状态 ===");
for (OrderStatus s : OrderStatus.values()) {
System.out.printf("code=%d, desc=%s%n", s.getCode(), s.getDesc());
}
// 3. 根据 code 反查(数据库场景)
OrderStatus fromDb = OrderStatus.of(3);
System.out.println("\n从数据库 code=3 反查: " + fromDb);
}
}
运行截图
对比
| 维度 | 魔法数字 | 枚举 |
|---|---|---|
| 可读性 | 需要查文档或注释 | 自描述 |
| 类型安全 | 任意 int 都能传 | 编译期检查 |
| 维护性 | 改一个值要全局搜索 | 改枚举定义一处即可 |
| 数据库映射 | 手动维护 | 通过 getCode() / of() 统一映射 |
场景 2:策略模式(替换大量 if/else)
问题:臃肿的分支判断
java
public BigDecimal calculatePrice(String role, BigDecimal amount) {
if ("VIP".equals(role)) {
return amount.multiply(new BigDecimal("0.9"));
} else if ("SVIP".equals(role)) {
return amount.multiply(new BigDecimal("0.75"));
} else if ("NORMAL".equals(role)) {
return amount;
}
// 每新增一个角色,就要改这里,违反开闭原则
throw new IllegalArgumentException("未知角色: " + role);
}
痛点:新增角色要改核心方法,容易出错,且逻辑和配置混在一起。
枚举策略重构
文件:DiscountStrategy.java
java
import java.math.BigDecimal;
public enum DiscountStrategy {
NORMAL("普通会员") {
@Override
public BigDecimal apply(BigDecimal amount) {
return amount; // 无折扣
}
},
VIP("VIP会员") {
@Override
public BigDecimal apply(BigDecimal amount) {
return amount.multiply(new BigDecimal("0.9")); // 9折
}
},
SVIP("SVIP会员") {
@Override
public BigDecimal apply(BigDecimal amount) {
return amount.multiply(new BigDecimal("0.75")); // 75折
}
};
private final String roleName;
DiscountStrategy(String roleName) {
this.roleName = roleName;
}
public abstract BigDecimal apply(BigDecimal amount);
public String getRoleName() {
return roleName;
}
}
文件:StrategyDemo.java
java
import java.math.BigDecimal;
public class StrategyDemo {
public static void main(String[] args) {
BigDecimal amount = new BigDecimal("1000");
System.out.println("=== 不同角色折扣计算 ===");
for (DiscountStrategy strategy : DiscountStrategy.values()) {
BigDecimal finalPrice = strategy.apply(amount);
System.out.printf("%s: 原价 %s → 实付 %s%n",
strategy.getRoleName(), amount, finalPrice);
}
}
}
运行截图
对比
| 维度 | if/else 版本 | 枚举策略版本 |
|---|---|---|
| 新增角色 | 修改核心类 | 新增枚举常量即可 |
| 单元测试 | 测一个长方法 | 每个策略独立测试 |
| 可读性 | 逻辑堆在一起 | 策略自描述 |
| 扩展性 | 违反开闭原则 | 符合开闭原则 |
如果策略逻辑很复杂,也可以在枚举里只存一个
Supplier<Strategy>或函数接口,把具体实现类延迟加载。
场景 3:统一返回码(后端接口必备)
问题:错误码散落各处
java
// Controller A
return new Result(500, "服务器内部错误", null);
// Controller B
return new Result(500, "系统错误", null);
// 同样的 500,消息不一样,前端无法统一处理
枚举统一封装
文件:ResultCode.java
java
public enum ResultCode {
SUCCESS(200, "操作成功"),
BAD_REQUEST(400, "请求参数错误"),
UNAUTHORIZED(401, "未登录或 token 过期"),
FORBIDDEN(403, "无权限访问"),
NOT_FOUND(404, "资源不存在"),
INTERNAL_ERROR(500, "服务器内部错误"),
SERVICE_BUSY(503, "服务繁忙,请稍后重试");
private final int code;
private final String message;
ResultCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() { return code; }
public String getMessage() { return message; }
// 统一包装成 Result 对象
public <T> Result<T> toResult() {
return new Result<>(this.code, this.message, null);
}
public <T> Result<T> toResult(T data) {
return new Result<>(this.code, this.message, data);
}
}
文件:Result.java
java
public class Result<T> {
private int code;
private String message;
private T data;
public Result(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
// 快速成功
public static <T> Result<T> ok(T data) {
return ResultCode.SUCCESS.toResult(data);
}
// 快速失败
public static <T> Result<T> error(ResultCode resultCode) {
return resultCode.toResult();
}
public int getCode() { return code; }
public String getMessage() { return message; }
public T getData() { return data; }
}
文件:ResultCodeDemo.java
java
public class ResultCodeDemo {
public static void main(String[] args) {
// 模拟成功响应
Result<String> success = Result.ok("用户数据");
System.out.println("成功响应:");
System.out.println(" code: " + success.getCode());
System.out.println(" message: " + success.getMessage());
System.out.println(" data: " + success.getData());
System.out.println();
// 模拟失败响应
Result<String> error = Result.error(ResultCode.NOT_FOUND);
System.out.println("失败响应:");
System.out.println(" code: " + error.getCode());
System.out.println(" message: " + error.getMessage());
System.out.println(" data: " + error.getData());
}
}
运行截图
对比
| 维度 | 硬编码返回 | 枚举统一返回 |
|---|---|---|
| 语义一致性 | 各处写法可能不同 | 一处定义,全局复用 |
| 维护成本 | 改消息要全局搜索 | 改枚举一处即可 |
| 国际化 | 难以扩展 | 可在枚举里扩展 getMessage(Locale) |
| 前端协作 | 错误码语义不清 | 前后端按枚举约定对接 |
完整可运行代码
把以下 7 个文件放在同一个包(或同一个文件夹)下:
| 文件名 | 类型 | 说明 |
|---|---|---|
OrderStatus.java |
enum |
场景 1 枚举定义 |
OrderStatusDemo.java |
class |
场景 1 运行入口(右键运行) |
DiscountStrategy.java |
enum |
场景 2 枚举定义 |
StrategyDemo.java |
class |
场景 2 运行入口(右键运行) |
ResultCode.java |
enum |
场景 3 枚举定义 |
Result.java |
class |
场景 3 泛型包装类 |
ResultCodeDemo.java |
class |
场景 3 运行入口(右键运行) |
注意:只有带 main 方法的 *Demo.java 才能直接运行,枚举文件本身没有 main 方法,无法直接运行。
总结
三个场景速查表
| 场景 | 判断标准 | 核心收益 |
|---|---|---|
| 状态/类型定义 | 一组固定值,且需要附带描述或码值 | 类型安全、可读性强、数据库映射方便 |
| 策略模式 | 多分支逻辑,且分支可能新增 | 消灭 if/else,符合开闭原则 |
| 统一返回码 | 前后端协作,需要集中管理错误语义 | 错误码标准化,降低维护成本 |
如果对你有帮助,欢迎点赞和收藏。有任何问题或更好的实践,欢迎在评论区交流。