责任链 vs 规则树:如何构建更强大的规则引擎?

以游戏租号应用的订单处理流程为例,系统需要根据订单类型(租赁、商城、会员)执行不同的业务规则。在租赁订单的处理中,需要检查库存、判断是否预约、应用优惠券,最后完成支付;而商城订单则只需要检查库存、应用优惠券,最后完成支付;会员订单则需要计算权益、应用优惠券,最终完成支付。如何优雅地管理这些复杂的业务规则,同时确保代码的可维护性和扩展性?

1. 传统责任链模式的局限性

责任链模式 是一种常见的设计模式,适用于线性流程的业务场景,例如日志处理、请求拦截、审批流等。在游戏租号的订单处理中,我们可以使用责任链模式,将各个订单处理环节(库存检查、会员折扣、优惠券、支付)串联起来,让请求依次经过各个处理器: 每个处理器执行自己的逻辑,并决定是否继续传递请求。

代码实现

1. 订单上下文

java 复制代码
@Data
public class OrderContext {
    private Order order;
}

2. 订单处理器接口

java 复制代码
public interface OrderHandler {

    /**
     * 规则树处理
     *
     * @param orderContext 订单上下文
     * @param chain 执行器
     */
    void handle(OrderContext orderContext, OrderHandlerChain chain);

}

3. 责任链执行器

java 复制代码
@Slf4j
@Component
public class OrderHandlerChain {

    @Resource
    private List<OrderHandler> handlers;
    private int index = 0;

    public void proceed(OrderContext context) {
        if (index < handlers.size()) {
            handlers.get(index++).handle(context, this);
        }
    }
}

4. 库存处理

java 复制代码
@Slf4j
@Component
@Order(1)
public class StockCheckHandler implements OrderHandler {

    @Override
    public void handle(OrderContext orderContext, OrderHandlerChain chain) {
        log.info("进入租赁库存检查: {}", JSON.toJSONString(orderContext));
        // 判断订单类型
        PaymentOrderTypeEnum orderType = PaymentOrderTypeEnum.valueOfByType(orderContext.getOrder().getOrderType()); 
        if (orderType == PaymentOrderTypeEnum.RENTAL) { 
            // TODO 租赁逻辑
            // 判断库存情况,决定是否预约
            if (/* 库存不足 */) {
                log.info("库存不足,进行预约");
                // TODO 预约逻辑
            } else {
                log.info("库存充足,直接下单");
                // TODO 非预约逻辑
            }
        } else if (orderType == PaymentOrderTypeEnum.MALL) { 
            // TODO 商城逻辑
        } else if (orderType == PaymentOrderTypeEnum.MEMBER) { 
            // TODO 会员逻辑
        }
   

        chain.proceed(orderContext);
    }
}

5. 优惠券处理

java 复制代码
@Slf4j
@Component
@Order(2)
public class CouponHandler implements OrderHandler {

    @Override
    public void handle(OrderContext orderContext, OrderHandlerChain chain) {
        log.info("进入优惠券处理器: {}", JSON.toJSONString(orderContext));
        // TODO 优惠券处理
        chain.proceed(orderContext);
    }
    
}

6. 支付处理

java 复制代码
@Slf4j
@Component
@Order(3)
public class PaymentHandler implements OrderHandler {

    @Override
    public void handle(OrderContext orderContext, OrderHandlerChain chain) {
        // TODO 支付处理
        chain.proceed(orderContext);
    }
}

然而,责任链模式的局限性 也很明显:

  1. 线性执行,无法跳跃或分支 ------ 订单处理流程并不是单一的顺序,而是会根据订单类型和是否预约走不同的流程。
  2. 规则增加后,链条可能变得过长 ------ 责任链适用于流程固定的场景,但当业务规则越来越复杂时,代码可读性和性能都会下降。
  3. 缺乏灵活性 ------ 任何新增的处理逻辑,都需要改动链条上的代码,难以动态调整规则。

2. 规则树模式:灵活的决策流

为了解决责任链的局限性,我们可以引入 规则树模式 ,用树状结构替代线性执行。规则树的核心思想是:每个节点代表一个决策点,后续执行路径由业务规则动态决定,而不是简单的顺序执行。

在我们的订单处理场景中,订单首先会被订单分流处理器 分类:

  • 如果是租赁订单,进入库存检查处理器,再根据是否预约进入不同的分支。
  • 如果是商城订单,直接进入商城库存处理器。
  • 如果是会员订单,进入会员逻辑。

代码实现

1. 订单上下文对象

java 复制代码
@Data
public class OrderContext {

    /**
     * 订单对象
     */
    private Order order;

    /**
     * 是否预约
     */
    private boolean isReserve;

}

2. 订单规则树处理接口

java 复制代码
public interface OrderHandler {

    /**
     * 规则树处理
     *
     * @param orderContext 订单上下文
     */
    void handle(OrderContext orderContext);

    /**
     * 获取下一个处理器
     *
     * @return 下一个处理器
     */
    OrderHandler next(OrderContext orderContext);

}

3. 订单规则树抽象类

java 复制代码
public abstract class AbstractOrderHandler implements OrderHandler {

    /**
     * 判断是否存在下一个处理器
     *
     * @param orderContext 上下文
     * @return 是否存在下一个处理器
     */
    public boolean hasNext(OrderContext orderContext) {
        return next(orderContext) != null;
    }

}

4. 规则树执行器

java 复制代码
@Slf4j
@Component
public class OrderHandlerChain {

    // 首节点
    @Resource
    private OrderShuntHandler orderShuntHandler;

    @Resource
    private PlatformTransactionManager platformTransactionManager;

    /**
     * 执行规则树
     *
     * @param context 上下文
     */
    public void proceed(OrderContext context) {
        TransactionStatus transaction = platformTransactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            // 执行规则树
            OrderHandler currentHandler = orderShuntHandler;
            while (currentHandler != null) {
                currentHandler.handle(context);
                currentHandler = currentHandler.next(context);
            }
            
            platformTransactionManager.commit(transaction);
            
        } catch (Exception e) {
            log.error("订单规则树执行异常: {}", e.getMessage(), e);
            platformTransactionManager.rollback(transaction);
            throw e;
        }
    }

}

5. 订单类型分流处理器

java 复制代码
@Slf4j
@Component
public class OrderShuntHandler extends AbstractOrderHandler {

    @Resource
    private RentStockCheckHandler rentStockCheckHandler;

    @Resource
    private MallStockCheckHandler mallStockCheckHandler;

    @Resource
    private MemberHandler memberHandler;

    @Override
    public void handle(OrderContext orderContext) {
        log.info("进入租赁与商城订单分流: {}", JSON.toJSONString(orderContext));
        // TODO 业务实现
    }

    @Override
    public OrderHandler next(OrderContext orderContext) {
        PaymentOrderTypeEnum orderType = PaymentOrderTypeEnum.valueOfByType(orderContext.getOrder().getOrderType());
        if (orderType == PaymentOrderTypeEnum.RENTAL) {
            return rentStockCheckHandler;
        } else if (orderType == PaymentOrderTypeEnum.MALL) {
            return mallStockCheckHandler;
        } else if (orderType == PaymentOrderTypeEnum.MEMBER) {
            return memberHandler;
        }
        return null;
    }

}

6. 租赁库存校验

java 复制代码
@Slf4j
@Component
public class RentStockCheckHandler extends AbstractOrderHandler {

    @Resource
    private ReserveHandler reserveHandler;

    @Resource
    private NoReserveHandler noReserveHandler;

    @Override
    public void handle(OrderContext orderContext) {
        log.info("进入租赁库存处理器: {}", JSON.toJSONString(orderContext));
        // 是否预约
        orderContext.setReserve(true);
        // TODO 业务实现
    }

    @Override
    public OrderHandler next(OrderContext orderContext) {
        if (orderContext.isReserve()) {
            return reserveHandler;
        }
        return noReserveHandler;
    }

}

7. 预约 与 非预约处理器

java 复制代码
@Slf4j
@Component
public class ReserveHandler extends AbstractOrderHandler {

    @Resource
    private CouponHandler couponHandler;

    @Override
    public void handle(OrderContext orderContext) {
        log.info("进入预约处理器: {}", JSON.toJSONString(orderContext));
        // TODO 业务实现
    }

    @Override
    public OrderHandler next(OrderContext orderContext) {
        return couponHandler;
    }
}

--------------------------------------------------------------------------

@Slf4j
@Component
public class NoReserveHandler extends AbstractOrderHandler {

    @Resource
    private CouponHandler couponHandler;

    @Override
    public void handle(OrderContext orderContext) {
          log.info("进入非预约处理器: {}", JSON.toJSONString(orderContext));
          // TODO 业务实现
    }

    @Override
    public OrderHandler next(OrderContext orderContext) {
        return couponHandler;
    }
}

8. 优惠券处理器

java 复制代码
@Slf4j
@Component
public class CouponHandler extends AbstractOrderHandler {

    @Resource
    private PaymentHandler paymentHandler;

    @Override
    public void handle(OrderContext orderContext) {
        log.info("进入优惠券处理器: {}", JSON.toJSONString(orderContext));
        // TODO 业务实现
    }

    @Override
    public OrderHandler next(OrderContext orderContext) {
        return paymentHandler;
    }

}

9. 支付处理器

java 复制代码
@Slf4j
@Component
public class PaymentHandler extends AbstractOrderHandler {

    @Override
    public void handle(OrderContext orderContext) {
        log.info("进入支付处理器: {}", JSON.toJSONString(orderContext));
        // TODO 业务实现
    }

    @Override
    public OrderHandler next(OrderContext orderContext) {
        return null;
    }

}

10. 商城库存处理器

java 复制代码
@Slf4j
@Component
public class MallStockCheckHandler extends AbstractOrderHandler {

    @Resource
    private CouponHandler couponHandler;

    @Override
    public void handle(OrderContext orderContext) {
        log.info("进入商城库存处理器: {}", JSON.toJSONString(orderContext));
        // 业务处理
    }

    @Override
    public OrderHandler next(OrderContext orderContext) {
        return couponHandler;
    }

}

11. 会员处理器

java 复制代码
@Slf4j
@Component
public class MemberHandler extends AbstractOrderHandler {

    @Resource
    private CouponHandler couponHandler;

    @Override
    public void handle(OrderContext orderContext) {
        log.info("进入会员处理器: {}", JSON.toJSONString(orderContext));
    }

    @Override
    public OrderHandler next(OrderContext orderContext) {
        return couponHandler;
    }

}

12. 测试

java 复制代码
public class OrderTest {

    @Resource
    private OrderHandlerChain orderHandlerChain;

    @Test
    public void test() {
        Order order = new Order();
        order.setId("1");
        order.setOrderNo("220123123883");
        order.setOrderType(PaymentOrderTypeEnum.RENTAL.getType());
        order.setPayType(PaymentWayEnum.BALANCE_PAYMENT.getWay());
        order.setOrderUserId("qfxl");
        order.setCouponId("");
        
        OrderContext orderContext = new OrderContext();
        orderContext.setOrder(order);
        
        orderHandlerChain.proceed(orderContext);
    }

}

租赁非预约

租赁预约

商城

会员流程:

规则树模式的优势

相较于传统的责任链模式,规则树模式提供了更灵活的决策路径,适用于复杂的业务场景

更灵活的流程控制 :支持不同路径的分支处理,而不是一条链条执行到底。

更好的扩展性 :新增规则只需增加节点,不需要改动其他代码。

更高的执行效率 :避免无意义的遍历,提升处理速度。

总结

在订单处理系统中,责任链模式适用于简单的线性流程,而规则树模式更适用于复杂的业务规则 。如果你的系统涉及多种决策逻辑,规则树模式将是更优的选择

你在业务中是否遇到过类似的规则决策问题?可以试试用规则树优化! 🚀

相关推荐
tech_zjf10 分钟前
装饰器:给你的代码穿上品如的衣服
前端·typescript·代码规范
JohnYan18 分钟前
工作笔记 - btop安装和使用
后端·操作系统
我愿山河人间20 分钟前
Dockerfile 和 Docker Compose:容器化世界的两大神器
后端
掘金码甲哥20 分钟前
golang倒腾一款简配的具有请求排队功能的并发受限服务器
后端
重庆穿山甲24 分钟前
装饰器模式实战指南:动态增强Java对象的能力
后端
卑微小文31 分钟前
企业级IP代理安全防护:数据泄露风险的5个关键防御点
前端·后端·算法
lovebugs35 分钟前
如何保证Redis与MySQL双写一致性?分布式场景下的终极解决方案
后端·面试
斑鸠喳喳1 小时前
模块系统 JPMS
java·后端
kunge20131 小时前
【手写数字识别】之数据处理
后端