《程序猿之设计模式实战 · 策略模式》
📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍
文章目录
- 写在前面的话
- 策略模式介绍
- 代码实现
- Spring 改进版本
- Spring 中的策略模式
- 补充说明
- 总结陈词

写在前面的话
近期,无论是编码还是休闲阶段,偶尔都会刷到关于设计模式的相关内容,发掘可以整理的内容还不少,就想着归纳高低给它整一个专栏。网上介绍23种设计模式的内容也不少,大多枯燥而冗长,这边不会重复搬砖,也不会按顺序介绍所有设计模式,只会从实战层面去分享几个实用的。
好了,废话不多说,先开始最常用的策略模式吧。
题外话
有的人认为设计模式很有用,遵循了面向对象等开发原则,可以提升代码复用,提高可维护性,便于后期的功能扩展。
有的人则认为设计模式很鸡肋,业务需求直接用代码快速实现就行了,哪里想这么多,会导致过度设计设计,反而浪费了时间成本,增加了代码量,对团队开发的要求也较高。
博主认为:
1、设计模式在业务开发中有其独特的价值,但是否使用以及如何使用,需要根据具体的项目需求、团队经验和开发环境来权衡。
2、我们不要过度追求严格遵循设计模式的标准,而应该贴合实际开发场景,以提升可维护性和可扩展性为目的导向,去合理使用设计模式。
3、在企业实战开发中,框架封装人员更应该注重设计模式的复用,而业务开发人员按需使用即可。
最后,为什么学设计模式,主要是学习一种编程思想,总结起来,也无非是学以致用罢了。
策略模式介绍
Tips:为保证技术连贯,照例先来一段技术简介,了解一下。
基础概念:
策略模式(Strategy Pattern)是一种行为设计模式,它定义了一系列算法(策略),并将每一个算法封装起来,使它们可以互相替换。策略模式使得算法的变化独立于使用算法的使用者。
策略模式的核心思想是将一组相关的算法封装成独立的策略类,并通过一个上下文类来使用这些策略。这样,客户端可以在运行时选择不同的策略,而不需要修改上下文类的代码。
组成部分:
1、策略接口(Strategy):定义一个公共接口,所有具体策略都需要实现这个接口。
2、具体策略(ConcreteStrategy):实现策略接口的具体算法。
3、上下文(Context):持有一个策略对象的引用,并通过该策略对象来调用具体的算法。
主要优势:
1、灵活性:可以在运行时选择不同的策略,增加了系统的灵活性。
2、开闭原则:可以在不修改现有代码的情况下增加新的策略。
3、清晰的职责分离:将算法的实现与使用分开,使得代码更加清晰。
个人理解:
策略可以理解为方式/方法之类的,即处理问题需要采用哪种不同的方法。
策略模式可以有效地替代 if…else 语句,尤其是在需要根据不同条件选择不同算法或行为的场景中。
最典型的运用示例就是多种付款方式,支付宝、微信、银联的,下面代码以此展开。
代码实现
如下所示,这里先上一个最普通的代码,实现一下策略模式,帮助大家理解,整体看起来还是挺清爽的。
不过代码量并没有貌似不会比if...else少多少?而且使用的时候,额外还要一个个new吗?
new的方式后面会改进,至于代码量而言,你要看这段代码在未来的易扩展性。
Tips:哪里听过类似的,长平之罪,罪在将来。好像不是一个意思。
Step1、定义支付策略接口
public interface PaymentStrategy {void pay(int amount);
}
Step2、定义具体策略类
Tips:这里@Component(“alipay”)注解非必须,是后面Spring阶段使用。
@Component("alipay")
public class Alipay implements PaymentStrategy {@Overridepublic void pay(int amount) {System.out.println("使用支付宝支付: " + amount + "元");}
}@Component("wechatPay")
public class WeChatPay implements PaymentStrategy {@Overridepublic void pay(int amount) {System.out.println("使用微信支付: " + amount + "元");}
}@Component("unionPay")
public class UnionPay implements PaymentStrategy {@Overridepublic void pay(int amount) {System.out.println("使用银联支付: " + amount + "元");}
}
Step3、编写支付上下文类
ublic class PaymentContextCommon {private final PaymentStrategy paymentStrategy;public PaymentContextCommon(PaymentStrategy paymentStrategy) {this.paymentStrategy = paymentStrategy;}public void executePayment(int amount) {paymentStrategy.pay(amount);}
}
Step4、客户端测试
public static void main(String[] args) {int amount = 100;// 使用支付宝支付PaymentContextCommon alipayContext = new PaymentContextCommon(new Alipay());alipayContext.executePayment(amount);// 使用微信支付PaymentContextCommon weChatContext = new PaymentContextCommon(new WeChatPay());weChatContext.executePayment(amount);// 使用银联支付PaymentContextCommon unionPayContext = new PaymentContextCommon(new UnionPay());unionPayContext.executePayment(amount);}
Spring 改进版本
针对前面的普通版本,我们可以使用Spring的依赖注入功能来管理支付策略的列表,会更简洁。
Step1、改进版本的支付上下文
这里采用Spring依赖注入Map的方式,减少了很多new的代码量。
这边还需要给
Tips:关于注入还有很多额外技巧和学问,这里先不展开,后续专题介绍。
public class PaymentContextSpring {private final Map<String, PaymentStrategy> paymentStrategies;public PaymentContextSpring(Map<String, PaymentStrategy> paymentStrategies) {this.paymentStrategies = paymentStrategies;}public void executePayment(Integer payType, int amount) {String beanName = PayTypeEnum.fromCode(payType).getImpl();PaymentStrategy strategy = paymentStrategies.get(beanName);if (strategy != null) {strategy.pay(amount);} else {System.out.println("未找到支付方式: " + beanName);}}
}@Configuration
public class TestConfig {@Beanpublic PaymentContextSpring paymentContextSpring(Map<String, PaymentStrategy> paymentStrategies) {return new PaymentContextSpring(paymentStrategies);}
}
Step2、定义一个支付枚举类
这边用枚举好处多多,清晰又明了。当然如果能接受前端直接传递bean的名称,也可以不要枚举。
public enum PayTypeEnum {WEIXIN_SCAN(1, "微信扫码支付", "wxScanPay"),ALIPAY_SCAN(2, "支付宝扫码支付", "aliScanPay"),UNION_PAY(3, "银联支付", "unionPay");private final Integer code;private final String desc;private final String impl;PayTypeEnum(Integer code, String desc, String impl) {this.code = code;this.desc = desc;this.impl = impl;}public static PayTypeEnum fromCode(Integer code) {for (PayTypeEnum payType : PayTypeEnum.values()) {if (payType.getCode().equals(code)) {return payType;}}throw new IllegalArgumentException("Invalid code: " + code);}
}
Step3、测试类改版
后续要扩展支付方式,就增加一个Service和修改枚举即可。
@SpringBootTest
@RunWith(SpringRunner.class)
public class PaymentTestSpring {//@Autowired//private PaymentContextSpring paymentContext;//这种方式也可以@Testpublic void testPayment() {PaymentContextSpring paymentContext = SpringUtil.getBean(PaymentContextSpring.class);int amount = 100;paymentContext.executePayment(1, amount);paymentContext.executePayment(2, amount);paymentContext.executePayment(3, amount);}
}
Spring 中的策略模式
在Spring框架中,策略模式被广泛应用于多个模块,使得框架具有高度的灵活性和可扩展性。通过这种设计,开发者可以在不修改核心代码的情况下,轻松地替换或扩展功能。
有几个重要的地方可以体现这一设计模式:
- Spring的事务管理
在Spring的事务管理中,PlatformTransactionManager接口定义了事务管理的策略。不同的数据库或事务管理机制(如JDBC、Hibernate、JPA等)可以实现这个接口,从而提供不同的事务管理策略。通过配置,Spring可以在运行时选择合适的事务管理策略。
- Spring的消息转换
Spring的消息转换机制(如MessageConverter)允许你在不同的消息格式之间进行转换。你可以定义多个具体的消息转换器(如JSON、XML等),并在运行时选择合适的转换器来处理消息。
- Spring的缓存抽象
Spring的缓存抽象(如CacheManager)允许你使用不同的缓存策略(如EhCache、Caffeine、Redis等)。你可以通过配置选择不同的缓存实现,而不需要修改使用缓存的代码。
- Spring的安全框架
在Spring Security中,认证和授权的策略也是通过策略模式实现的。不同的认证方式(如表单登录、OAuth、LDAP等)可以实现相同的接口,Spring Security会根据配置选择合适的认证策略。
- Spring的事件处理
Spring的事件处理机制(如ApplicationListener和ApplicationEvent)也可以视为策略模式的应用。不同的事件监听器可以实现相同的接口,从而处理不同类型的事件。
补充说明
上述介绍Spring方式,可以通过XMl、JavaBean或@Profile等实现策略切换。
当然,能实现目的,并且扩展性强就OK了。不需要纠结这种方式是否为标准策略模式、以及策略如何切换。
只要将策略定义为一个个接口,然后按需选择需要的策略就可以了。
总结陈词
💗 本篇文章介绍了策略模式的实战应用,希望可以帮助到大家。
💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。

相关文章:
《程序猿之设计模式实战 · 策略模式》
📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数…...
deepinlinux-v23用deepinunioncode初始c例子
deepinlinux-v23用deepinunioncode初始c例子 # deepinunioncode 新建duc工程cmake模版,开局提示 No CMAKE_CXX_COMPILER could be found错误记录 需要duc 左下角磁轮设置 设置cmake和gcc g的文件,如果本机装过了(apt install gc…...
前端框架对比选择:如何在众多技术中找到最适合你的
引言 在现代Web开发中,前端框架的选择对项目的成功与否至关重要。随着技术的不断发展,市面上涌现出多种前端框架,每种框架都有其独特的特点、优缺点以及适用场景。本文将对当前主流的前端框架进行详细对比,帮助开发者在选择时做出…...
数据结构—(java)反射,枚举,lambda表达式
文章目录 反射反射的定义:反射相关的类:反射相关的方法:反射示例:获取Class类对象创建指定类的对象反射私有属性:反射私有方法:反射私有的构造方法 枚举枚举的意义枚举类的实现枚举类的使用:Enu…...
机器学习(西瓜书)第 14 章 概率图模型
14.1 隐马尔可夫模型 机器学习最重要的任务,是根据一些已观察到的证据(例如训练样本)来对感兴趣的未知变量(例如类别标记)进行估计和推测。概率模型(probabilistic model)提供了一种描述框架&a…...
Python异步编程-asyncio详解
目录 asyncio简介示例什么是 asyncio?适用场景API asyncio的使用可等待对象什么是可等待对象?协程对象任务对象Future对象 协程什么是协程?基本使用运行协程 Task什么是 Task?创建 Task取消 TaskTask 异常获取Task 回调 TaskGroup什么是 Tas…...
UniApp如何打包成客户端应用程序
像flutter是支持PC宽屏、桌面平台(Windows/macOS/Linux),我一直在期望UniApp什么时候也支持PC,桌面平台,终于盼到了。 1、支持PC宽屏 从uni-app 2.9起,支持PC宽屏的适配。 uni-app提供的屏幕适配方案&am…...
你应该掌握的12条饭局规矩!
在职场的舞台上,饭局不仅仅是一场简单的聚餐,它是一场精心编排的社交盛宴,是展示个人魅力、构建人脉网络的重要平台。精通饭局的艺术,能让你在职场的交际中更加自如。以下是酱酒亮哥整理的12条饭局指南,希望你在职场的…...
【541. 反转字符串 II 简单】
题目: 给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。 如果剩余字符少于 k 个,则将剩余字符全部反转。如果剩余字符小于 2k 但大于或等于 k 个,…...
基于PHP的丽江旅游管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的丽江旅游管理系统 一 介绍 此丽江旅游系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈:phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销…...
vue3+Element-plus el-input 输入框组件二次封装(支持金额、整数、电话、小数、身份证、小数点位数控制,金额显示中文提示等功能)
一、效果图 二、组件集成了以下功能 1、输入金额--支持千分号显示、可设置decimalLimit来调整小数点位数 2、金额鼠标移入提示中文--标签添加isTip开启中文提示则不允许开启千分号显示showThousands 3、输入手机号--设置inputTypephone 4、输入整数---设置inputTypeinteger 5、…...
jQuery 简介 ③ ready()事件函数、jQuery 二个原则及容错机制
文章目录 jQuery 简介 ③五、ready() 准备就绪时执行代码六、jQuery 核心1、Get and Set in One 原则2、Get first Set all 原则3、容错机制:jQuery 简介 ③ 五、ready() 准备就绪时执行代码 如果我们在中引入jQuery库文件,并编写相应的jQuery代码来操作DOM元素。这很可能导…...
选择Alluxio来解决AI模型训练场景数据访问的五大理由
在AI模型训练尤其是大模型领域,存储系统的性能和稳定性直接决定了模型训练、推理、部署任务的效率和成本。随着全球AI行业的爆发带来的数据规模的快速增长,如何高效管理和利用这些数据成为AI模型训练中的一大挑战。 AI模型训练场景面临的五大难题 1. 数…...
POS共识机制简介
权益证明(Proof of Stake, PoS)共识机制基础 1. 引言 权益证明(Proof of Stake, PoS)是一种用于区块链网络的共识机制,旨在解决工作量证明(Proof of Work, PoW)机制中存在的能源消耗高、中心化…...
Spring为什么要用三级缓存解决循环依赖?
Spring为什么要用三级缓存解决循环依赖? 1. Spring是如何创建一个bean对象2. Spring三级缓存2.1 一级缓存:单例池,经历过完整bean生命,单例Bean对象2.2 二级缓存:提前暴露的Bean2.3 三级缓存:打破循环 3. S…...
【Redis入门到精通三】Redis核心数据类型(List,Set)详解
目录 Redis数据类型 编辑 1.List类型 (1)常见命令 (2)内部编码 2.Set类型 (1)常见命令 (2)内部编码 Redis数据类型 查阅Redis官方文档可知,Redis提供给用户的核…...
本科生如何学习机器学习
一、入门阶段 1. 数学与统计学基础 高等数学:学习微积分、极限、级数等基本概念。线性代数:掌握矩阵运算、特征值和特征向量、线性方程组等。概率论与统计学:理解概率分布、假设检验、贝叶斯定理等统计知识。 2. 编程语言学习 Python&…...
海康威视摄像机和录像机的监控与回放
文章目录 海康威视摄像机和录像机的监控与回放1、海康威视监控设备简介1.1、摄像机二次开发1.1.1:协议选择 1.2:web集成1.2:标准协议对接1.2.1:ffmpeg软件转流1.2.2:开源监控软件shinobi1.2.2.1 安装使用1.2.2.2 shino…...
校医务室健康服务系统小程序的设计
管理员账户功能包括:系统首页,个人中心,用户管理,医生管理,医患交流管理,预约医生管理,健康打卡管理,运动打卡管理,饮食打卡管理 微信端账号功能包括:系统首…...
MySQL 中的 UTF-8 与 UTF8MB4:差异解析
在 MySQL 数据库中,字符集的选择对于数据的存储和处理至关重要。其中,UTF-8 和 UTF8MB4 是两个常见的字符集选项。那么,它们之间到底有什么区别呢? 一、字符集简介 UTF-8 UTF-8(8-bit Unicode Transformation Format&…...
iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个生活电费的缴纳和查询小程序
一、项目初始化与配置 1. 创建项目 ohpm init harmony/utility-payment-app 2. 配置权限 // module.json5 {"requestPermissions": [{"name": "ohos.permission.INTERNET"},{"name": "ohos.permission.GET_NETWORK_INFO"…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
