在软件开发中,我们经常需要处理不同系统或模块间的状态转换。今天,我将通过一个电商订单与物流状态的转换案例,展示如何优雅地实现枚举间的互相转换。
场景描述
假设我们有一个电商系统,包含两个核心状态枚举:
- 订单状态(OrderStatusEnum) - 描述订单在电商系统中的生命周期
- 物流状态(ShippingStatusEnum) - 描述订单在物流系统中的流转状态
这两个状态密切相关但又不完全相同,我们需要在它们之间建立转换关系。
枚举定义
1. 订单状态枚举
/**
* 订单状态枚举
*/
public enum OrderStatusEnum {
UNPAID(0, "待支付"),
PAID(1, "已支付"),
PACKAGED(2, "已打包"),
SHIPPED(3, "已发货"),
DELIVERED(4, "已送达"),
COMPLETED(5, "已完成"),
CANCELLED(10, "已取消"),
REFUNDING(11, "退款中"),
REFUNDED(12, "已退款");
private final int code;
private final String description;
OrderStatusEnum(int code, String description) {
this.code = code;
this.description = description;
}
// 根据code获取枚举实例
public static OrderStatusEnum fromCode(int code) {
for (OrderStatusEnum status : values()) {
if (status.code == code) {
return status;
}
}
throw new IllegalArgumentException("无效的订单状态码: " + code);
}
// 转换为物流状态
public ShippingStatusEnum toShippingStatus() {
switch (this) {
case UNPAID:
case PAID:
case CANCELLED:
case REFUNDING:
case REFUNDED:
return null; // 这些订单状态没有对应的物流状态
case PACKAGED:
return ShippingStatusEnum.PREPARING;
case SHIPPED:
return ShippingStatusEnum.IN_TRANSIT;
case DELIVERED:
return ShippingStatusEnum.DELIVERED;
case COMPLETED:
return ShippingStatusEnum.COMPLETED;
default:
throw new IllegalStateException("未知的订单状态: " + this);
}
}
// getters
public int getCode() { return code; }
public String getDescription() { return description; }
}
2. 物流状态枚举
/**
* 物流状态枚举
*/
public enum ShippingStatusEnum {
PREPARING(1, "准备中"),
IN_TRANSIT(2, "运输中"),
DELIVERED(3, "已送达"),
COMPLETED(4, "已完成"),
RETURNING(10, "退货中"),
RETURNED(11, "已退货");
private final int code;
private final String description;
ShippingStatusEnum(int code, String description) {
this.code = code;
this.description = description;
}
// 根据code获取枚举实例
public static ShippingStatusEnum fromCode(int code) {
for (ShippingStatusEnum status : values()) {
if (status.code == code) {
return status;
}
}
throw new IllegalArgumentException("无效的物流状态码: " + code);
}
// 转换为订单状态
public OrderStatusEnum toOrderStatus() {
switch (this) {
case PREPARING:
return OrderStatusEnum.PACKAGED;
case IN_TRANSIT:
return OrderStatusEnum.SHIPPED;
case DELIVERED:
return OrderStatusEnum.DELIVERED;
case COMPLETED:
return OrderStatusEnum.COMPLETED;
case RETURNING:
return OrderStatusEnum.REFUNDING;
case RETURNED:
return OrderStatusEnum.REFUNDED;
default:
throw new IllegalStateException("未知的物流状态: " + this);
}
}
// getters
public int getCode() { return code; }
public String getDescription() { return description; }
}
关键设计点解析
-
状态映射关系:
- 不是所有订单状态都有对应的物流状态(如待支付、已取消等)
- 物流状态可能触发订单状态的变更(如退货中对应退款中)
-
转换方法设计:
- 每个枚举类内部实现转换方法,知道如何转换为另一种状态
- 方法命名直观:
toShippingStatus()
和toOrderStatus()
-
null值处理:
- 当没有对应状态时返回null,调用方需要处理这种情况
-
异常处理:
- 对非法状态码抛出明确的异常信息
使用示例
// 订单状态转物流状态
OrderStatusEnum orderStatus = OrderStatusEnum.SHIPPED;
ShippingStatusEnum shippingStatus = orderStatus.toShippingStatus();
System.out.println("订单状态: " + orderStatus.getDescription()
+ " → 物流状态: " + (shippingStatus != null ? shippingStatus.getDescription() : "无"));
// 物流状态转订单状态
ShippingStatusEnum shippingStatus = ShippingStatusEnum.RETURNING;
OrderStatusEnum orderStatus = shippingStatus.toOrderStatus();
System.out.println("物流状态: " + shippingStatus.getDescription()
+ " → 订单状态: " + orderStatus.getDescription());
最佳实践建议
-
保持转换逻辑简单:如果转换逻辑变得复杂,考虑引入状态模式
-
文档化映射关系:在枚举类或项目文档中明确记录状态映射关系
-
单元测试:为状态转换编写单元测试,确保转换逻辑正确
-
考虑性能:对于高频调用的场景,可以缓存转换结果
-
扩展性:设计时考虑未来可能新增的状态
总结
通过在枚举内部实现状态转换方法,我们实现了:
- 高内聚的设计:转换逻辑与状态定义在一起
- 易于维护:状态变更时只需修改一处
- 类型安全:编译时检查状态转换
- 代码清晰:直观的转换方法调用
这种模式不仅适用于订单和物流状态,还可以应用于各种需要状态转换的场景,如工作流状态、支付状态等。