《程序猿之设计模式实战 · 策略模式》
📢 大家好,我是 【战神刘玉栋】,有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&…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Spring数据访问模块设计
前面我们已经完成了IoC和web模块的设计,聪明的码友立马就知道了,该到数据访问模块了,要不就这俩玩个6啊,查库势在必行,至此,它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据(数据库、No…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...
PHP 8.5 即将发布:管道操作符、强力调试
前不久,PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5!作为 PHP 语言的又一次重要迭代,PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是,借助强大的本地开发环境 ServBay&am…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...
python爬虫——气象数据爬取
一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...
