基于多设计模式的状态扭转设计:策略模式与责任链模式的实战应用

简介: 接下来,我会结合实战案例,聊聊如何用「策略模式 + 责任链模式」构建灵活可扩展的状态引擎,让抽奖系统的状态管理从「混乱战场」变成「有序流水线」。

1.前言

你有没有遇到过这样的情况:活动刚上线就发现奖品库存没配置,紧急暂停后用户还能参与;或者奖品从「可兑换」改成「已售罄」时,后台接口报了一堆空指针?这些问题的根源,其实和我们办线下活动时的筹备逻辑很像。

比如公司年会筹备,得先确认场地(活动是否合规)、再检查物料(奖品库存)、最后安排人员(用户参与限制),每个环节环环相扣,顺序错了就容易出乱子。抽奖系统的状态管理也是同理:活动启停、奖品状态变更、用户参与资格校验,这些状态流转如果处理不好,代码就会变成「一锅粥」。

未用设计模式前的典型问题:用 if-else 硬编码状态判断(比如 if (status == 0) { ... } else if (status == 1) { ... }),新增状态时要改多处代码,改着改着就出现「牵一发动全身」的 bug,维护成本直线上升。

这也是为什么我们需要一套更优雅的解决方案。接下来,我会结合实战案例,聊聊如何用「策略模式 + 责任链模式」构建灵活可扩展的状态引擎,让抽奖系统的状态管理从「混乱战场」变成「有序流水线」。


插播一条消息~

🔍十年经验淬炼 · 系统化AI学习平台推荐

  • 📚 完整知识体系:从数学基础 → 工业级项目(人脸识别/自动驾驶/GANs),内容由浅入深
  • 💻 实战为王:每小节配套可运行代码案例(提供完整源码)
  • 🎯 零基础友好:用生活案例讲解算法,无需担心数学/编程基础

🚀 特别适合

  • 想系统补强AI知识的开发者
  • 转型人工智能领域的从业者
  • 需要项目经验的学生

2.正文

2.1遇到的问题

在抽奖系统状态管理的常规设计中,我们常常会陷入代码臃肿、维护困难的困境。通过分析典型的反面案例,我们可以清晰地看到四个核心问题正在制约系统的灵活性与可扩展性。

1. 多对象顺序维护困难

活动与奖品的状态转换存在强依赖关系(如:必须先更新奖品状态为「已发放」,才能将活动状态改为「已结束」)。常规设计将这些逻辑硬编码在单一方法中,通过多层if-else控制执行顺序:

// 常规设计中的顺序依赖问题
public void reverseStatus(Long activityId, String activityTargetStatus, 
                         Long prizeId, String prizeTargetStatus) {
    // 先更新奖品状态
    if (prizeTargetStatus != null) {
        prizeMapper.updateStatus(activityId, prizeId, prizeTargetStatus);
    }
    // 再更新活动状态(依赖奖品状态已更新)
    if (activityTargetStatus != null) {
        // 检查奖品是否全部发放
        if (prizeMapper.countUnissued(activityId) == 0) {
            activityMapper.updateStatus(activityId, activityTargetStatus);
        }
    }
}

image.gif

这种设计的风险在于:顺序调整需要修改核心逻辑,新增「用户状态校验」步骤时,可能破坏原有依赖关系。

2. 动态算法变更成本高

状态转换条件(如「活动结束的判断标准」)直接写在业务代码中,当规则变化(如从「奖品发完即结束」改为「时间到即结束」)时,需要穿透多层代码修改条件判断:

// 硬编码的状态转换条件
if (activityDO.getEndTime().before(new Date()) 
    || prizeMapper.countInitPrize(activityId) <= 0) {
    // 状态转换逻辑
}

image.gif

3. 系统扩展性不足

新增状态类型(如「活动预热中」状态)时,需要修改所有相关的if-else判断,违背「开闭原则」:

// 扩展性问题示例
if (status.equals("INIT")) {
    // 初始化逻辑
} else if (status.equals("ACTIVE")) {
    // 活动中逻辑
} else if (status.equals("PREHEAT")) { // 新增状态需修改此处
    // 预热逻辑
}

image.gif

4. 处理请求复杂性高

单个方法承担多种职责(活动状态更新+奖品状态更新+用户状态校验),导致方法冗长、参数众多:

// 职责混乱的方法签名
public void updateStatus(Long activityId, String activityStatus,
                        Long prizeId, String prizeStatus,
                        List<Long> userIds, String userStatus) {
    // 混杂多种状态处理逻辑
}

image.gif

常规设计的优缺点对比可通过以下表格直观呈现:

特点 优点 缺点
实现方式 简单直接,初期开发快 硬编码,高耦合,维护困难
扩展性 新增状态转换需修改原有代码
可读性 差,嵌套多 逻辑复杂,不易理解
灵活性 状态转换条件变更困难

核心痛点总结:常规设计通过硬编码将状态转换逻辑集中在单一方法中,导致代码职责混乱、耦合度高,既无法应对业务规则的动态变化,也难以支持新功能的快速迭代。这种“一次性”代码架构,在系统复杂度提升后会成为严重的性能与维护瓶颈。


2.2两种设计模式

2.2.1策略模式

你是否遇到过这样的场景:网购付款时选择支付宝或微信支付,系统会自动切换不同的支付流程?这背后其实藏着策略模式的智慧——将不同算法(支付方式)封装成独立策略,让它们能灵活替换,且变化不影响使用方。在编程世界里,策略模式定义了算法族的封装规范,确保算法变化独立于使用算法的客户。

在抽奖系统设计中,我们把状态转换逻辑拆分为独立策略。每个 Operator 类(如 ActivityOperator 处理活动状态、PrizeOperator 管理奖品状态、UserOperator 控制用户参与状态)都是具体策略,它们共同继承 AbstractActivityOperator 抽象类,实现 convert(执行转换)和 needConvert(判断是否需要转换)方法。这种设计的妙处在于:新增状态转换规则时,只需添加新的 Operator 类,完全不用修改已有代码,完美契合开闭原则

下面的 UML 类图清晰展示了这种结构:抽象类定义统一接口,具体策略类各自实现差异化逻辑,形成可扩展的策略家族。

image.gif 编辑

核心价值:通过策略模式,抽奖系统的状态转换逻辑从臃肿的条件判断中解放出来,每个策略职责单一、可独立测试,未来新增规则时只需"插插件"式扩展,大幅降低维护成本。


2.2.2责任链模式

责任链模式通过构建接收者对象链,让多个对象依次处理请求,从而避免请求发送者与接收者的直接耦合。生活中最贴切的类比是工厂流水线:原材料依次经过质检、包装、入库等环节,每个工位仅负责特定任务,完成后自动传递给下一环节,既保证流程有序,又实现各环节独立运作。

在抽奖系统状态引擎中,这一模式通过 sequence() 方法定义处理顺序(如 PrizeOperatorUserOperatorsequence 为 1,ActivityOperator 为 2),再由 processConvertStatus 方法按序号依次调度。该方法会遍历当前序列的所有 Operator,检查是否需要转换(needConvert()),若需转换则执行 convert() 操作,完成后从处理队列中移除该节点。这种设计使顺序严格可控(按序号执行),且节点高度解耦(各 Operator 仅关注自身逻辑,无需知晓其他节点存在)。

核心优势

  • 顺序可控:通过 sequence 定义明确执行优先级,避免流程混乱
  • 节点解耦:新增或移除 Operator 无需修改其他节点代码,符合开闭原则
  • 职责单一:每个节点仅处理特定转换逻辑,降低维护复杂度

以下是责任链模式的 UML 序列图,展示请求从客户端到各 Operator 的完整处理流程:

image.gif 编辑


2.3具体设计

抽奖系统状态引擎的目录结构采用模块化划分,核心代码按职责边界清晰分离:

  • operator 文件夹:存放所有策略实现类,如 ActivityOperator(活动状态管理)、PrizeOperator(奖品发放逻辑)、UserOperator(用户资格校验)等,每个类专注于单一策略维度的状态处理。
  • impl 文件夹:包含状态管理的核心实现,如 ActivityStatusManagerImpl(状态引擎管理器)、DefaultSequenceProvider(责任链顺序配置)等,负责协调策略执行与流程控制。
  • model 文件夹:定义基础数据模型,如 EventContext(事件上下文)、StatusResult(状态处理结果),统一各模块数据交互标准。
service/
└── activitystatus/
    ├── impl/
    │   └── ActivityStatusManagerImpl.java  // 状态管理器实现
    ├── operator/
    │   ├── AbstractActivityOperator.java   // 策略抽象类
    │   ├── ActivityOperator.java           // 活动状态策略
    │   ├── PrizeOperator.java              // 奖品状态策略
    │   └── UserOperator.java               // 用户状态策略
    └── ActivityStatusManager.java          // 状态管理器接口

image.gif

策略与责任链的融合设计

系统采用“策略接口 + 责任链执行”的双层架构:

  1. 策略接口抽象:定义 IStatusOperator 接口,包含 handle(处理事件)和 rollback(回滚操作)方法,所有具体策略类通过实现接口完成定制化逻辑。
  2. 责任链动态编排ActivityStatusManagerImpl 通过 operatorMap 维护策略类与执行顺序的映射(key 为 sequence 序号,value 为 operator 实例),事件触发时按 sequence 升序依次调用 handle 方法,形成可配置的处理流水线。

这种设计既保留了策略模式对算法的封装性,又通过责任链实现了流程的动态组合,解决了传统硬编码流程难以扩展的问题。

核心代码实现

状态引擎的核心逻辑集中在 ActivityStatusManagerImpl 的事件处理方法中,代码通过getOrderedOperators 确保策略按预设顺序执行,每个 operator 仅关注自身逻辑,管理器无需感知具体实现,实现了“控制反转”。为应对流程中断场景,系统设计了反向责任链回滚回滚时按策略执行的逆序调用 rollback 方法,确保数据一致性(如先回滚奖品发放,再取消活动状态变更)。:

@Component
public class ActivityStatusManagerImpl implements ActivityStatusManager {
    private static final Logger logger = LoggerFactory.getLogger(ActivityStatusManagerImpl.class);
    @Autowired
    private final Map<String, AbstractActivityOperator> operatorMap = new HashMap<>();
    @Autowired
    private ActivityService activityService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handlerEvent(ConvertActivityStatusDTO convertActivityStatusDTO) {
        // 1、活动状态扭转有依赖性,导致代码维护性差
        // 2、状态扭转条件可能会扩展,当前写法,扩展性差,维护性差
        if(CollectionUtils.isEmpty(operatorMap)){
            logger.warn("operator 为空");
            return;
        }
        Map<String, AbstractActivityOperator> currMap = new HashMap<>(operatorMap);
        Boolean update = false;
        // 先处理:人员、奖品
        update = processConvertStatus(convertActivityStatusDTO, currMap, 1);
        // 后处理:活动
        update = processConvertStatus(convertActivityStatusDTO, currMap, 2) || update;
        // 更新缓存
        if (update) {
            activityService.cacheActivity(convertActivityStatusDTO.getActivityId());
        }
    }
    @Override
    public void rollbackHandlerEvent(ConvertActivityStatusDTO convertActivityStatusDTO) {
        // operatorMap:活动、奖品、人员
        // 活动是否需要回滚??绝对需要,
        // 原因:奖品都恢复成INIT,那么这个活动下的奖品绝对没抽完
        for (AbstractActivityOperator operator : operatorMap.values()) {
            operator.convert(convertActivityStatusDTO);
        }
        // 缓存更新
        activityService.cacheActivity(convertActivityStatusDTO.getActivityId());
    }
    private Boolean processConvertStatus(ConvertActivityStatusDTO convertActivityStatusDTO, Map<String, AbstractActivityOperator> currMap, int sequence) {
        Boolean update = false;
        //遍历currMap
        Iterator<Map.Entry<String, AbstractActivityOperator>> iterator = currMap.entrySet().iterator();
        while (iterator.hasNext()) {
            AbstractActivityOperator operator = iterator.next().getValue();
            // Operator 是否需要转换
            if (operator.sequence() != sequence
                    || !operator.needConvert(convertActivityStatusDTO)) {
                continue;
            }
            // 需要转换:转换
            if (!operator.convert(convertActivityStatusDTO)) {
                logger.error("{}状态转换失败!", operator.getClass().getName());
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_STATUS_CONVERT_ERROR);
            }
            // currMap 删除当前 Operator
            iterator.remove();
            update = true;
        }
        // 返回
        return update;
    }
}

image.gif

一个具体的operator实现:

@Component
public class ActivityOperator extends AbstractActivityOperator{
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Override
    public Integer sequence() {
        return 2;
    }
    @Override
    public boolean needConvert(ConvertActivityStatusDTO convertActivityStatusDTO) {
        Long activityId = convertActivityStatusDTO.getActivityId();
        ActivityStatusEnum targetStatus = convertActivityStatusDTO.getTargetActivityStatus();
        if(null == activityId
                || null == targetStatus){
            return false;
        }
        ActivityDO activityDO = activityMapper.selectById(activityId);
        if(null == activityDO){
            return false;
        }
        //当前活动状态与传入活动状态一致,不处理
        if(targetStatus.name().equalsIgnoreCase(activityDO.getStatus())){
            return false;
        }
        // 需要判断奖品是否全部抽完
        // 查询 INIT 状态的奖品个数
        int count = activityPrizeMapper.countPrize(activityId, ActivityPrizeStatusEnum.INIT.name());
        if (count > 0) {
            return false;
        }
        return true;
    }
    @Override
    public Boolean convert(ConvertActivityStatusDTO convertActivityStatusDTO) {
        try {
            activityMapper.updateStatus(convertActivityStatusDTO.getActivityId(), convertActivityStatusDTO.getTargetActivityStatus().name());
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}

image.gif

设计对比:常规方案 vs 最终方案

通过以下指标可直观看到架构优化效果:

设计维度 常规设计(硬编码流程) 最终设计(策略+责任链)
耦合度 ×(流程与逻辑强绑定) √(策略与流程解耦)
扩展性 ×(新增逻辑需修改主线) √(新增 Operator 即可)
维护性 ×(代码交织难以定位) √(单一职责便于调试)

整体架构类图

以下是核心类关系的UML类图:

image.gif 编辑


3.小结

在抽奖系统状态转换的复杂场景中,策略模式与责任链模式的协同配合展现了强大的设计价值。策略模式聚焦“做什么”,封装了不同状态转换的核心算法,让每种状态变更逻辑都能独立演化;责任链模式则解决“怎么做、按什么顺序做”,通过链式结构清晰定义状态流转的流程与优先级,二者共同构建起灵活可靠的状态引擎。

核心价值提炼:这种模式组合在活动频繁迭代、多状态联动的业务场景中尤为突出——不仅显著降低了代码耦合度,还让系统在新增状态规则或调整流程顺序时,无需大规模重构,极大提升了扩展性与维护性。无论是电商大促的阶梯式抽奖,还是营销活动的多阶段状态管理,都能通过这套架构实现高效的状态管控。

对于面临复杂状态转换难题的开发者而言,不妨借鉴这种“策略定算法+责任链控流程”的设计思路,让系统在应对业务变化时更具韧性。

相关文章
|
4天前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
1106 0
|
3天前
|
机器学习/深度学习 人工智能 前端开发
通义DeepResearch全面开源!同步分享可落地的高阶Agent构建方法论
通义研究团队开源发布通义 DeepResearch —— 首个在性能上可与 OpenAI DeepResearch 相媲美、并在多项权威基准测试中取得领先表现的全开源 Web Agent。
516 10
|
13天前
|
人工智能 运维 安全
|
12天前
|
人工智能 测试技术 API
智能体(AI Agent)搭建全攻略:从概念到实践的终极指南
在人工智能浪潮中,智能体(AI Agent)正成为变革性技术。它们具备自主决策、环境感知、任务执行等能力,广泛应用于日常任务与商业流程。本文详解智能体概念、架构及七步搭建指南,助你打造专属智能体,迎接智能自动化新时代。
|
4天前
|
弹性计算 Kubernetes jenkins
如何在 ECS/EKS 集群中有效使用 Jenkins
本文探讨了如何将 Jenkins 与 AWS ECS 和 EKS 集群集成,以构建高效、灵活且具备自动扩缩容能力的 CI/CD 流水线,提升软件交付效率并优化资源成本。
301 0
|
11天前
|
人工智能 异构计算
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
敬请锁定《C位面对面》,洞察通用计算如何在AI时代持续赋能企业创新,助力业务发展!
|
12天前
|
机器学习/深度学习 人工智能 自然语言处理
B站开源IndexTTS2,用极致表现力颠覆听觉体验
在语音合成技术不断演进的背景下,早期版本的IndexTTS虽然在多场景应用中展现出良好的表现,但在情感表达的细腻度与时长控制的精准性方面仍存在提升空间。为了解决这些问题,并进一步推动零样本语音合成在实际场景中的落地能力,B站语音团队对模型架构与训练策略进行了深度优化,推出了全新一代语音合成模型——IndexTTS2 。
806 23
|
4天前
|
缓存 供应链 监控
VVIC seller_search 排行榜搜索接口深度分析及 Python 实现
VVIC搜款网seller_search接口提供服装批发市场的商品及商家排行榜数据,涵盖热销榜、销量排名、类目趋势等,支持多维度筛选与数据分析,助力选品决策、竞品分析与市场预测,为服装供应链提供有力数据支撑。
|
4天前
|
缓存 监控 API
Amazon item_review 商品评论接口深度分析及 Python 实现
亚马逊商品评论接口(item_review)可获取用户评分、评论内容及时间等数据,支持多维度筛选与分页调用,结合Python实现情感分析、关键词提取与可视化,助力竞品分析、产品优化与市场决策。