设计模式之策略模式核心概念和应用场景

目录

一、深入理解策略模式的设计思想、掌握在其分布式消息场景中的应用价值

[1.1 什么是策略模式?](#1.1 什么是策略模式?)

[1.2 为什么要用策略模式?](#1.2 为什么要用策略模式?)

[1.3 策略模式解决方案](#1.3 策略模式解决方案)

[1.4 策略模式的核心价值](#1.4 策略模式的核心价值)

[1.5 策略模式的结构](#1.5 策略模式的结构)

[1.6 适用场景判断](#1.6 适用场景判断)

[1.7 注意事项](#1.7 注意事项)

二、多类型权益消息策略模式接口设计与工厂实现

[2.1 需求背景](#2.1 需求背景)

[2.2 架构设计](#2.2 架构设计)

[2.3 代码实现](#2.3 代码实现)

策略接口定义(common模块)

策略工厂实现

[2.4 注意事项](#2.4 注意事项)


一、深入理解策略模式的设计思想、掌握在其分布式消息场景中的应用价值

1.1 什么是策略模式?

  • 打个比方:策略模式就像"出行方式选择器"

    • 你要去某个目的地,可以选择:开车、坐地铁、打车、骑车、步行

    • 每种出行方式就是一条"策略"

    • 你只需要告诉选择器"我要去XX",选择器会根据情况帮你选最合适的方式

    • 你不需要关心每种方式的具体实现(开车怎么开、地铁怎么坐)

  • 生活中的策略模式:

    • 支付方式:微信、支付宝、银行卡、现金 → 支付时选择一种即可

    • 排序算法:快排、归并、堆排序 → 根据数据特点选择最优算法

    • 压缩方式:ZIP、RAR、7Z → 根据需求选择压缩策略

1.2 为什么要用策略模式?

  • 问题场景:假设有三种业务消息要发送
java 复制代码
// 传统写法:大量if-else
public void sendMessage(String type, Object data) {
    if ("RESUME".equals(type)) {
        // 简历分析的发送逻辑(20行代码)
    } else if ("INTERVIEW".equals(type)) {
        // 面试创建的发送逻辑(20行代码)
    } else if ("DIALOGUE".equals(type)) {
        // 对话消息的发送逻辑(20行代码)
    }
    // 后续新增业务类型,继续加else if...
}
  • 传统写法的痛点 :
    • 代码臃肿:所有逻辑堆在一个方法里,几百行代码

    • 职责混乱:简历逻辑、面试逻辑、对话逻辑混在一起

    • 改一处怕出错:修改简历逻辑,可能影响面试逻辑

    • 扩展困难:新增业务类型要改原代码,违反开闭原则

1.3 策略模式解决方案

1.4 策略模式的核心价值

  • 开闭原则:对扩展开放,对修改关闭

    • 新增业务类型:只需添加新的策略类,无需修改现有代码

    • 比如:要新增"直播面试"类型,只需加一个LiveInterviewStrategy类

  • 单一职责:每个策略类只负责一件事

    • ResumeStrategy只管简历消息

    • InterviewStrategy只管面试消息

    • 互不干扰,边界清晰

  • 便于测试:每个策略类可独立单元测试

  • 运行时切换:根据条件动态选择策略

1.5 策略模式的结构

1.6 适用场景判断

  • 适合用策略模式:

    • 有多个类只是行为差异,可以用策略来切换

    • 需要在运行时选择算法或策略

    • 业务有很多if-else分支,且分支条件固定

  • 不适合用策略模式:

    • 策略数量很少(1-2个),简单if-else就够了

    • 策略之间差异很小,没必要拆分

    • 策略经常变化,频繁新增删除

1.7 注意事项

  • 不要过度设计:简单场景用简单方案

  • 策略数量控制:策略过多时要考虑合并或拆分

  • 客户端要了解策略:调用方需要知道有哪些策略可用

二、多类型权益消息策略模式接口设计与工厂实现

2.1 需求背景

  • 业务MQ消息发送的挑战:

    • 业务类型多样:简历分析、面试创建等不同业务类型

    • 消息格式各异:每种业务需要不同的消息体结构

    • 路由规则不同:不同业务消息需要路由到不同队列

  • 策略模式的价值:

    • 开闭原则:新增业务类型只需添加策略实现,无需修改现有代码

    • 单一职责:每个策略类只负责一种消息发送逻辑

    • 解耦业务逻辑:调用方无需了解具体消息发送细节

2.2 架构设计

2.3 代码实现

策略接口定义(common模块)

java 复制代码
/**
 * 权益消息发送策略接口
 * 用于ai-service发送不同类型的业务MQ消息
 * 
 * 设计说明:
 * - 策略接口定义在common模块,便于共享
 * - 策略实现在ai-service,与业务逻辑内聚
 * - account-service只负责权益扣减和延迟检查,不发送业务MQ
 */
public interface BenefitMessageStrategy {
    
    /**
     * 发送业务消息
     * 由ai-service在权益扣减成功后调用
     * 
     * @param messageId 消息ID(任务ID,用于关联延迟检查)
     * @param businessId 业务ID(简历ID/面试ID)
     * @param accountId 账号ID
     */
    void sendBusinessMessage(String messageId, String businessId, Long accountId);
    
    /**
     * 获取策略类型(对应权益编码)
     * 
     * @return 权益编码
     */
    String getStrategyType();
}

策略工厂实现

java 复制代码
@Component
@Slf4j
public class BenefitMessageStrategyFactory {
    
    private final Map<String, BenefitMessageStrategy> strategyMap = new ConcurrentHashMap<>();
    
    /**
     * 构造函数注入所有策略实现
     * Spring会自动注入所有BenefitMessageStrategy的实现类
     */
    @Autowired
    public BenefitMessageStrategyFactory(List<BenefitMessageStrategy> strategies) {
        for (BenefitMessageStrategy strategy : strategies) {
            strategyMap.put(strategy.getStrategyType(), strategy);
            log.info("注册权益消息策略: {} -> {}", 
                    strategy.getStrategyType(), strategy.getClass().getSimpleName());
        }
    }
    
    /**
     * 根据权益编码获取策略
     * 支持精确匹配和类型匹配(面试类型统一处理)
     */
    public BenefitMessageStrategy getStrategy(String benefitCode) {
        // 1. 先精确匹配
        BenefitMessageStrategy strategy = strategyMap.get(benefitCode);
        if (strategy != null) {
            return strategy;
        }
        
        // 2. 面试类型使用统一的面试策略
        if (isInterviewType(benefitCode)) {
            strategy = strategyMap.get("INTERVIEW");
            if (strategy != null) {
                return strategy;
            }
        }
        
        throw new IllegalArgumentException("未找到权益编码对应的消息策略: " + benefitCode);
    }
    
    private boolean isInterviewType(String benefitCode) {
        try {
            BenefitEnum benefitEnum = BenefitEnum.valueOf(benefitCode);
            return benefitEnum == BenefitEnum.SPECIAL_INTERVIEW 
                    || benefitEnum == BenefitEnum.COMMON_INTERVIEW;
        } catch (Exception e) {
            return false;
        }
    }
}

2.4 注意事项

  • 接口放common模块:便于多个服务共享策略接口定义

  • 实现放ai-service:消息发送逻辑与业务服务内聚

  • 自动注册策略:利用Spring的List注入自动收集所有策略实现

  • 线程安全:使用ConcurrentHashMap保证并发安全

相关推荐
晴天的雨.99213 分钟前
【C++】继承篇
java·开发语言·c++
SomeOtherTime19 分钟前
第 1 讲:内存布局与所有权解剖——栈帧、胖指针与“移动/拷贝”在汇编层面的真实面目
java·开发语言·汇编
qq_4069810324 分钟前
c++ lamda(三)Lambda表达式
java·前端·c++
头发还在的女程序员24 分钟前
Java在线租房系统源码解析:Spring Boot+Vue 3+UniApp房源管理与预约看房方案
java·vue.js·spring boot·租房小程序·租房系统·租房软件
FYKJ_201031 分钟前
springboot同人周边订单管理系统48955-计算机毕业设计、课程设计
java·javascript·vue.js·spring boot·django·php·课程设计
Flynn Liang1 小时前
LLM 应用安全:Prompt 注入、越权与输入输出护栏
java·人工智能·大模型·langchain4j·llm 安全·prompt 注入·guardrail
特立独行的猫A7 小时前
用仓颉语言 cj-tauri 框架,五分钟给娃做了个鸿蒙形近字游戏
java·后端·python
码事漫谈10 小时前
我花了一周时间,看AI公司怎么"管"AI,结果越看越后背发凉
后端
真上帝的左手10 小时前
30. 技术专题-‌应用框架组件-分布式架构
分布式·架构·系统架构·分布式架构