【设计模式】三十二、策略模式

news2025/3/22 7:43:07

系列文章|源码

https://github.com/tyronczt/design-mode-learn

文章目录

    • 系列文章|源码
    • 一、模式定义与核心思想
    • 二、模式结构与Java实现
      • 1. 核心角色
      • 2. Java代码示例
    • 三、策略模式的五大核心优势
    • 四、适用场景
    • 五、与其他模式的对比
    • 六、最佳实践建议
    • 总结
  • 🚀进阶版【更符合实际开发】🚀
    • 策略+工厂模式
      • 效果图
      • 类图
      • 控制器
      • 策略工厂
      • 策略接口
      • 策略实现类
      • 枚举类
      • Spring工具类

一、模式定义与核心思想

策略模式(Strategy Pattern) 是一种行为型设计模式,其核心思想是将一组算法或行为封装为独立的策略类,使得它们可以相互替换,且算法的变化独立于使用它的客户端。该模式通过组合替代继承,符合开闭原则(对扩展开放、对修改关闭)。

二、模式结构与Java实现

1. 核心角色

策略接口(Strategy):定义算法族的公共行为(如PaymentStrategy接口)。
具体策略类(Concrete Strategy):实现接口的具体算法(如微信支付、支付宝支付)。
上下文类(Context):持有策略引用,动态切换算法(如PaymentContext类)。

2. Java代码示例

以下以支付方式选择为例展示实现:

// 1. 策略接口(定义支付行为)
public interface IPaymentStrategy {
    void pay(double amount);
}

// 2. 具体策略类(微信支付)
public class WeChatPayStrategy implements IPaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("微信支付:" + amount + "元");
    }
}

// 3. 具体策略类(支付宝支付)
public class AliPayStrategy implements IPaymentStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("支付宝支付:" + amount + "元");
    }
}

// 4. 上下文类(管理策略)
public class PaymentContext {
    private IPaymentStrategy strategy;

    public void setStrategy(IPaymentStrategy strategy) {
        this.strategy = strategy;
    }

    public void executePayment(double amount) {
        if (strategy != null) {
            strategy.pay(amount);
        } else {
            throw new IllegalStateException("未选择支付方式");
        }
    }
}

// 5. 客户端调用
public class Client {
    public static void main(String[] args) {
        PaymentContext context = new PaymentContext();
        context.setStrategy(new WeChatPayStrategy());
        context.executePayment(100.0);  // 输出:微信支付:100.0元
        
        context.setStrategy(new AliPayStrategy());
        context.executePayment(200.0);  // 输出:支付宝支付:200.0元
    }
}

三、策略模式的五大核心优势

  1. 消除复杂条件分支
    • 传统方式需通过大量if-else判断不同场景(如支付方式选择),策略模式将每个算法封装为独立类,代码更清晰。
  2. 符合开闭原则(OCP)
    扩展性高:新增策略(如银行卡支付)只需添加新类,无需修改现有代码。
    维护性高:修改某一策略的逻辑不会影响其他策略。
  3. 动态切换算法
    • 运行时通过setStrategy()方法灵活切换策略(如根据用户选择切换支付方式)。
  4. 算法复用与解耦
    • 不同上下文可共享同一策略(如多个订单系统共用支付策略)。
    • 算法实现与业务逻辑解耦,便于单元测试。
  5. 减少重复代码
    • 公共逻辑可抽取至抽象类(如支付前的参数校验),具体策略类仅关注核心算法。

四、适用场景

  1. 多算法动态选择:如支付方式、数据加密算法、排序算法等。
  2. 业务规则频繁变化:如电商促销策略(满减、折扣、积分)。
  3. 替代复杂条件分支:避免多个if-elseswitch-case语句。

五、与其他模式的对比

模式核心区别典型场景
策略模式关注算法族动态切换支付方式、折扣策略
状态模式关注状态驱动的行为变化订单状态流转、游戏角色
工厂模式关注对象创建策略类的实例化管理

六、最佳实践建议

结合工厂模式:使用策略工厂统一管理策略实例(如通过Map存储策略类)。
默认策略:在上下文中设置默认策略以避免空指针异常。
策略组合:复杂场景可将多个策略组合使用(如先满减再折扣)。

总结

策略模式通过封装算法动态切换机制,显著提升了代码的可维护性与扩展性。在Java中,结合接口与多态特性,能高效实现复杂的业务需求(如电商支付、促销活动)。其核心价值在于将“做什么”与“怎么做”分离,是应对算法灵活性和业务变化的利器。

🚀进阶版【更符合实际开发】🚀

策略+工厂模式

效果图

实际开发中只要前端传入支付类型,后端即可根据策略进行相应支付逻辑处理。

类图

控制器

@RestController
public class AdvancedClientController {
    @Resource
    private PaymentHandleStrategyFactory paymentHandlerFactory;

    @GetMapping("/pay")
    public void pay(int code, double amount) {
        String paymentType = PaymentEnum.getPaymentNameByCode(code);
        if (paymentType != null) {
            // 根据支付方式选择支付处理器
            IPaymentHandleStrategy paymentHandleStrategy = paymentHandlerFactory.getPaymentHandleStrategy(paymentType);
            paymentHandleStrategy.pay(amount);
        }
    }
}

策略工厂

@Slf4j
@Service
public class PaymentHandleStrategyFactory {

    /**
     * 根据支付类型获取相应的处理策略
     *
     * @param type 支付类型,用于确定使用哪种策略处理支付请求
     * @return IPaymentHandleStrategy 实现类,用于处理特定支付类型的策略
     */
    public IPaymentHandleStrategy getPaymentHandleStrategy(String type) {
        // 根据传入的类型参数选择并返回对应的支付处理策略
        // AliPay+HandleStrategyImpl
        IPaymentHandleStrategy iPaymentHandleStrategy = SpringContextUtil.getBean(type + "HandleStrategyImpl");
        // 检查 iPaymentHandleStrategy 是否为空,并增加防御性编程
        if (iPaymentHandleStrategy == null || StringUtils.isEmpty(iPaymentHandleStrategy)) {
            log.warn("getDatasetHandleStrategy error. type:{}", type);
            throw new RuntimeException("getDatasetHandleStrategy error. type:" + type);
        }
        return iPaymentHandleStrategy;
    }
}

策略接口

public interface IPaymentHandleStrategy {
    void pay(double amount);
}

策略实现类

@Service
public class AliPayHandleStrategyImpl implements IPaymentHandleStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("【策略+工厂】支付宝支付:" + amount + "元");
    }
}

@Service
public class WeChatPayHandleStrategyImpl implements IPaymentHandleStrategy {
    @Override
    public void pay(double amount) {
        System.out.println("【策略+工厂】微信支付:" + amount + "元");
    }
}

枚举类

package com.tyron.design.mode.learn.advanced.enums;

public enum PaymentEnum {

    // 微信支付
    WE_CHAT_PAY(1, "weChatPay", "微信支付"),
    // 支付宝支付
    ALI_PAY(2, "aliPay", "支付宝支付");


    private int code;
    private String name;
    private String desc;

    PaymentEnum(int code, String name, String desc) {
        this.code = code;
        this.name = name;
        this.desc = desc;
    }

    public int getCode() {
        return code;
    }

    public String getName() {
        return name;
    }

    public String getDesc() {
        return desc;
    }

    /**
     * 根据code获取枚举常量
     */

    public static PaymentEnum getPaymentEnumByCode(int code) {
        for (PaymentEnum paymentEnum : PaymentEnum.values()) {
            if (paymentEnum.getCode() == code) {
                return paymentEnum;
            }
        }
        return null;
    }

    /**
     * 根据code获取枚举常量的name属性
     *
     * @param code
     * @return
     */
    public static String getPaymentNameByCode(int code) {
        for (PaymentEnum paymentEnum : PaymentEnum.values()) {
            if (paymentEnum.getCode() == code) {
                return paymentEnum.getName();
            }
        }
        return null;
    }
}

Spring工具类

package com.tyron.design.mode.learn.util;

import lombok.NonNull;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component("SpringUtil")
public class SpringContextUtil implements ApplicationContextAware {
    private static ApplicationContext context;

    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    @SuppressWarnings("unchecked")
    public static <T> T getBean(String beanName) {
        if (context.containsBean(beanName)) {
            return (T)context.getBean(beanName);
        } else {
            return null;
        }
    }
}

详细代码已上传 Github:https://github.com/tyronczt/design-mode-learn/tree/main/design-mode-learn-3_2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2319426.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Cyberchef实用功能之-json line格式文件美化和查询

本文将介绍一下如何使用cyberchef对json line格式数据进行美化方便阅读&#xff0c;以及json line格式数据的批量查询操作。 之前的文章介绍了json格式数据的美化和查询&#xff0c;即Cyberchef实用功能之-json解析美化和转换&#xff0c;Cyberchef实用功能之-批量提取json数据…

计算机基础:编码03,根据十进制数,求其原码

专栏导航 本节文章分别属于《Win32 学习笔记》和《MFC 学习笔记》两个专栏&#xff0c;故划分为两个专栏导航。读者可以自行选择前往哪个专栏。 &#xff08;一&#xff09;WIn32 专栏导航 上一篇&#xff1a;计算机基础&#xff1a;编码02&#xff0c;有符号数编码&#xf…

FaryGui文字shader修改,弧线排列

因项目要求,希望将文字进行标题那样的弧线排列,如下图: 对FaryGUI的文字Shader进行了一些修改,基本达到要求,shader设置如下: shader代码如下: // Upgrade NOTE: replaced _Object2World with unity_ObjectToWorld // Upgrade NOTE: replaced mul(UNITY_MATRIX_MVP,*) with Un…

C++ string的模拟实现

Hello!!大家早上中午晚上好&#xff0c;昨天复习了string的使用&#xff0c;今天来模拟实现一下string&#xff01;&#xff01;&#xff01; 一、string的框架搭建 1.1首先我们需要一个string的头文件用来做变量、函数、类等声明&#xff1b;再需要一个test文件来做测试,还需…

使用LangChain实现基于LLM和RAG的PDF问答系统

目录 前言一.大语言模型(LLM)1. 什么是LLM&#xff1f;2. LLM 的能力与特点 二、增强检索生成(RAG)三. 什么是 LangChain&#xff1f;1. LangChain 的核心功能2. LangChain 的优势3. LangChain 的应用场景4. 总结 四.使用 LangChain 实现基于 PDF 的问答系统 前言 本文将介绍 …

C++实现rabbitmq生产者消费者

RabbitMQ是一个开源的消息队列系统&#xff0c;它实现了高级消息队列协议&#xff08;AMQP&#xff09;&#xff0c; 特点 可靠性&#xff1a;通过持久化、镜像队列等机制保证消息不丢失&#xff0c;确保消息可靠传递。灵活的路由&#xff1a;提供多种路由方式&#xff0c;如…

在VMware上部署【Ubuntu】

镜像下载 国内各镜像站点均可下载Ubuntu镜像&#xff0c;下面例举清华网站 清华镜像站点&#xff1a;清华大学开源软件镜像站 | Tsinghua Open Source Mirror 具体下载步骤如下&#xff1a; 创建虚拟机 准备&#xff1a;在其他空间大的盘中创建存储虚拟机的目录&#xff0c…

又双叒叕Scrapy爬虫相关的面试题及详细解答

Scrapy是Python开发的一个快速、高层次的网络爬虫框架,专注于高效抓取网页并提取结构化数据。其核心设计基于异步处理机制,适合大规模数据采集任务。 文章目录 基础概念1. Scrapy框架的核心组件有哪些?架构与流程2. 描述Scrapy的工作流程核心组件详解3. 如何自定义Item Pipe…

使用STM32CubeMX+DMA+空闲中断实现串口接收和发送数据(STM32G070CBT6)

1.STM32CubeMX配置 &#xff08;1&#xff09;配置SYS &#xff08;2&#xff09;配置RCC &#xff08;3&#xff09;配置串口&#xff0c;此处我用的是串口4&#xff0c;其他串口也是一样的 &#xff08;4&#xff09;配置DMA&#xff0c;将串口4的TX和RX添加到DMA中 &#…

【视觉提示学习】3.21论文随想

. . Frontiers of Information Technology & Electronic Engineering. 2024, 25(1): 42-63 https://doi.org/10.1631/FITEE.2300389 中文综述&#xff0c;根据里面的架构&#xff0c;把视觉提示学习分成两类&#xff0c;一类是单模态提示学习&#xff08;以vit为代表&…

(一)丶Windows安装RabbitMQ可能会遇到的问题

一丶可能会忘了配置ERLang的环境变量 二丶执行命令时报错 第一步 rabbitmq-plugins enable rabbitmq_management 第二部 rabbitmqctl status 三丶修改.erlang.cookie 文件 1.找到C盘目下的.erlang.cookie文件 C:\Users\admin\.erlang.cookie C:\Windows\System32\config\sys…

Mistral AI发布开源多模态模型Mistral Small 3.1:240亿参数实现超越GPT-4o Mini的性能

法国人工智能初创公司Mistral AI于2025年3月正式推出新一代开源模型Mistral Small 3.1 &#xff0c;该模型凭借240亿参数的轻量级设计&#xff0c;在多项基准测试中表现优异&#xff0c;甚至超越了Google的Gemma 3和OpenAI的GPT-4o Mini等主流专有模型。 1、核心特性与优势 多…

springboot整合mybatis-plus【详细版】

目录 一&#xff0c;简介 1. 什么是mybatis-plus2.mybatis-plus特点 二&#xff0c;搭建基本环境 1. 导入基本依赖&#xff1a;2. 编写配置文件3. 创建实体类4. 编写controller层5. 编写service接口6. 编写service层7. 编写mapper层 三&#xff0c;基本知识介绍 1. 基本注解 T…

Qt之MVC架构MVD

什么是MVC架构&#xff1a; MVC模式&#xff08;Model–view–controller&#xff09;是软件工程中的一种软件架构模式&#xff0c;把软件系统分为三个基本部分&#xff1a;模型&#xff08;Model&#xff09;、视图&#xff08;View&#xff09;和控制器&#xff08;Controll…

深度解析学术论文成果评估(Artifact Evaluation):从历史到现状

深度解析学术论文成果评估(Artifact Evaluation)&#xff1a;从历史到现状 引言 在计算机科学和工程领域的学术研究中&#xff0c;可重复性和可验证性越来越受到重视。随着实验性研究的复杂性不断增加&#xff0c;确保研究成果可以被其他研究者验证和构建变得尤为重要。这一需…

二分查找上下界问题的思考

背景 最近在做力扣hot100中的二分查找题目时&#xff0c;发现很多题目都用到了二分查找的变种问题&#xff0c;即二分查找上下界问题&#xff0c;例如以下题目&#xff1a; 35. 搜索插入位置 74. 搜索二维矩阵 34. 在排序数组中查找元素的第一个和最后一个位置 它们不同于查找…

关于FastAPI框架的面试题及答案解析

FastAPl是一个现代、快速(高性能)的Web框架,用于构建API,基于Python3.7+的类型提示功能。它由Python开发者SebastianRamirez创建,并且使用了Starlette作为其核心组件以及Pydantic进行数据验证。 文章目录 基础篇1. FastAPI的核心优势是什么?2. 如何定义一个GET请求路由?…

HashMap添加元素的流程图

文章目录 JDK7 vs JDK8 的 HashMap 结构变化Java8 中哈希表的红黑树优化机制HashMap 添加元素的完整流程解析1. 计算 key 的哈希值并确定索引2. 检查该索引位置是否已有元素3. 处理哈希冲突4. 判断当前存储结构&#xff08;链表还是红黑树&#xff09;5. 判断链表长度是否超过 …

面向医药仓储场景下的药品分拣控制策略方法 研究(大纲)

面向医药仓储场景下的药品分拣控制策略方法研究 基于多机器人协同与智能调度的分拣系统设计 第一章 绪论 1.1 研究背景与意义 医药仓储自动化需求&#xff1a; 人工分拣效率低、出错率高&#xff08;如药品批次混淆、过期风险&#xff09;温控药品&#xff08;如疫苗、生物制…

AI大模型介绍

大模型介绍 大模型是指具有大规模参数和复杂计算结构的机器学习模型&#xff0c;通常由深度神经网络构建而成&#xff0c;拥有数十亿甚至数千亿个参数 开发大模型不是从0开始&#xff0c;是建立在已有的大模型基座模型上做开发&#xff0c;构建企业知识库&#xff08;向量数据库…