在复杂业务工作流中,上下文变量需要在多个处理节点之间传递,这些变量可能存在为空的情况,传统的判空方式会导致代码冗余,还可能遗漏判空逻辑引发空指针异常。Optional类可以有效解决这类问题,让上下文变量的传递更安全规范。
Optional类基础介绍
Optional是一个可以为null的容器对象,它可以保存类型为T的值,或者仅仅保存null。使用Optional可以让我们不需要显式进行空值检查,通过提供的方法可以更安全地处理可能为空的变量。
常用核心方法
- of(T value):创建一个非空的Optional对象,如果传入的值为null会直接抛出异常
- ofNullable(T value):创建一个Optional对象,允许传入的值为null
- isPresent():判断Optional中是否包含非空值
- get():获取Optional中的值,如果值为空会抛出异常
- orElse(T other):如果Optional中有值则返回该值,否则返回传入的默认参数
- map(Function<? super T, ? extends U> mapper):如果Optional有值,则对其执行传入的函数映射,返回新的Optional对象
业务工作流上下文变量传递的问题
假设我们有一个订单处理的业务工作流,包含参数校验、库存检查、价格计算、订单落库四个节点,每个节点都需要传递用户信息、商品信息、优惠信息等上下文变量。传统实现方式下,每个节点都需要对传入的变量做空值判断,代码会非常冗余。
// 传统上下文变量传递方式
public class OrderContext {
private String userId;
private String productId;
private Double discount;
// 省略getter和setter
}
public class OrderProcessWorkflow {
public void process(OrderContext context) {
// 参数校验节点
if (context == null) {
throw new IllegalArgumentException("上下文不能为空");
}
if (context.getUserId() == null) {
throw new IllegalArgumentException("用户ID不能为空");
}
// 库存检查节点
if (context.getProductId() == null) {
throw new IllegalArgumentException("商品ID不能为空");
}
// 价格计算节点
Double finalPrice = 100.0;
if (context.getDiscount() != null) {
finalPrice = finalPrice * context.getDiscount();
}
// 后续逻辑
}
}
上面的代码中每个节点都有大量的空值判断逻辑,如果工作流节点增多,这些判空代码会分散在各个节点中,维护成本很高,还容易遗漏判空导致异常。
使用Optional优化上下文传递
我们可以将上下文中的变量用Optional封装,在工作流传递过程中统一处理空值情况,减少冗余的判空代码。
封装上下文变量为Optional
首先修改上下文类,将可能为空的变量用Optional包装:
import java.util.Optional;
public class OptionalOrderContext {
private final Optional<String> userId;
private final Optional<String> productId;
private final Optional<Double> discount;
private OptionalOrderContext(Optional<String> userId, Optional<String> productId, Optional<Double> discount) {
this.userId = userId;
this.productId = productId;
this.discount = discount;
}
// 静态工厂方法创建上下文
public static OptionalOrderContext of(String userId, String productId, Double discount) {
return new OptionalOrderContext(
Optional.ofNullable(userId),
Optional.ofNullable(productId),
Optional.ofNullable(discount)
);
}
// 获取用户的Optional封装
public Optional<String> getUserId() {
return userId;
}
// 获取商品ID的Optional封装
public Optional<String> getProductId() {
return productId;
}
// 获取折扣的Optional封装
public Optional<Double> getDiscount() {
return discount;
}
}
工作流节点使用Optional处理上下文
各个工作流节点直接使用Optional提供的方法处理上下文变量,不需要额外的空值判断:
import java.util.Optional;
public class OptionalOrderProcessWorkflow {
// 参数校验节点
public Optional<OptionalOrderContext> validateNode(OptionalOrderContext context) {
// 校验用户ID是否存在
Optional<String> validUserId = context.getUserId()
.filter(id -> id.length() > 0);
if (!validUserId.isPresent()) {
System.out.println("用户ID校验失败");
return Optional.empty();
}
// 校验商品ID是否存在
Optional<String> validProductId = context.getProductId()
.filter(id -> id.length() > 0);
if (!validProductId.isPresent()) {
System.out.println("商品ID校验失败");
return Optional.empty();
}
return Optional.of(context);
}
// 库存检查节点
public Optional<OptionalOrderContext> stockCheckNode(OptionalOrderContext context) {
// 模拟库存检查逻辑,假设商品ID为product_1时有库存
boolean hasStock = context.getProductId()
.map(id -> "product_1".equals(id))
.orElse(false);
if (!hasStock) {
System.out.println("库存不足");
return Optional.empty();
}
return Optional.of(context);
}
// 价格计算节点
public Optional<Double> priceCalculateNode(OptionalOrderContext context) {
double basePrice = 100.0;
// 如果有折扣则计算折扣价,否则返回原价
return context.getDiscount()
.map(discount -> basePrice * discount)
.or(() -> Optional.of(basePrice));
}
// 完整工作流执行
public void process(OptionalOrderContext context) {
Optional<OptionalOrderContext> validatedContext = validateNode(context);
Optional<OptionalOrderContext> stockCheckedContext = validatedContext.flatMap(this::stockCheckNode);
Optional<Double> finalPrice = stockCheckedContext.flatMap(this::priceCalculateNode);
finalPrice.ifPresent(price -> {
System.out.println("订单最终价格:" + price);
// 后续订单落库等逻辑
});
}
public static void main(String[] args) {
OptionalOrderProcessWorkflow workflow = new OptionalOrderProcessWorkflow();
// 测试正常场景
OptionalOrderContext normalContext = OptionalOrderContext.of("user_1", "product_1", 0.8);
workflow.process(normalContext);
// 测试无折扣场景
OptionalOrderContext noDiscountContext = OptionalOrderContext.of("user_2", "product_1", null);
workflow.process(noDiscountContext);
// 测试缺商品ID场景
OptionalOrderContext invalidContext = OptionalOrderContext.of("user_3", null, 0.8);
workflow.process(invalidContext);
}
}
注意事项
- 不要为了使用Optional而强行使用,对于明确不会为空的变量,不需要用Optional封装,避免增加不必要的复杂度
- 不要直接在Optional上调用
get()方法,除非你已经确定Optional中有值,否则建议使用orElse、ifPresent等安全的方法 - Optional不适合作为类的字段或者方法参数长期使用,更适合作为方法返回值和临时变量使用,上面的上下文封装场景属于特殊情况,需要结合实际需求判断
- 在复杂工作流中,可以结合函数式编程的链式调用,让上下文传递的逻辑更简洁,减少嵌套的判空代码
总结
在复杂业务工作流中,使用Optional类封装上下文变量,可以有效减少冗余的空值判断代码,避免空指针异常的发生。通过Optional提供的链式调用和安全处理方法,我们可以让上下文变量的传递逻辑更清晰,代码的可维护性更高。实际开发中需要结合业务场景合理使用Optional,避免过度设计带来的额外复杂度。