【设计模式】策略模式定义及其实现代码示例
文章目录
- 一、策略模式
- 1.1 策略模式的定义
- 1.2 策略模式的参与者
- 1.3 策略模式的优点
- 1.4 策略模式的缺点
- 1.5 策略模式的使用场景
- 二、策略模式简单实现
- 2.1 案例描述
- 2.2 实现代码
- 三、策略模式的代码优化
- 3.1 优化思路
- 3.2 抽象策略接口
- 3.3 上下文
- 3.4 具体策略实现类
- 3.5 测试
- 参考资料
完整案例代码:java-demos/design-pattern-demos/strategy-pattern at main · idealzouhu/java-demos
一、策略模式
1.1 策略模式的定义
策略模式(Strategy Pattern)定义如下:
Define a family of algorithms,encapsulate each one,and make them interchangeable.(定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。)
1.2 策略模式的参与者
策略模式的参与者主要有:
- Context(上下文类):它持有一个
Strategy对象,用于调用具体策略的方法。客户端通常只需要与Context交互,而不直接接触具体的策略实现。 - Strategy(策略接口):定义了算法的通用接口,所有具体策略都实现这个接口。
- ConcreteStrategy(具体策略类):实现
Strategy接口的具体算法类,不同的策略类提供不同的算法实现。

1.3 策略模式的优点
- 避免使用多重条件判断:策略模式可以取代
if-else或switch-case的条件分支。
1.4 策略模式的缺点
- 策略数量可能会增多:如果策略太多,会增加类的数量,维护成本上升。如果 if-else 判断分支不多并且是固定的,那就使用策略模式即可。
1.5 策略模式的使用场景
- 算法/行为自由切换: 比如支付系统中,可以通过策略模式动态选择不同的支付方式(微信支付、支付宝支付、信用卡支付等)。
- 屏蔽算法底层细节:只用知道算法名字即可
二、策略模式简单实现
2.1 案例描述
假设我们有一个系统,它可以根据不同的需求选择不同的排序算法,比如快速排序、冒泡排序、归并排序等。通过策略模式,我们可以将这些不同的算法作为策略类进行实现。
2.2 实现代码
// 策略接口
interface SortStrategy {void sort(int[] array);
}// 具体策略类:快速排序
class QuickSortStrategy implements SortStrategy {@Overridepublic void sort(int[] array) {// 快速排序算法实现System.out.println("Using Quick Sort");// 排序逻辑}
}// 具体策略类:冒泡排序
class BubbleSortStrategy implements SortStrategy {@Overridepublic void sort(int[] array) {// 冒泡排序算法实现System.out.println("Using Bubble Sort");// 排序逻辑}
}// 上下文类:负责使用策略
class SortingContext {private SortStrategy strategy;// 设置策略public void setStrategy(SortStrategy strategy) {this.strategy = strategy;}// 执行排序public void executeSort(int[] array) {strategy.sort(array);}
}// 测试
public class StrategyPatternExample {public static void main(String[] args) {SortingContext context = new SortingContext();// 使用快速排序策略context.setStrategy(new QuickSortStrategy());context.executeSort(new int[]{3, 1, 2});// 使用冒泡排序策略context.setStrategy(new BubbleSortStrategy());context.executeSort(new int[]{3, 1, 2});}
}
三、策略模式的代码优化
3.1 优化思路
优化思路为使用 @Component 修饰具体策略类,从而让 Spring 提供的 IoC 容器自动添加策略类,从而实现开闭原则。
注意事项,如果你的策略实现类被初始化的时间晚于 onApplicationEvent 方法的调用,可能会导致在注册策略时未能找到这些策略。确保所有策略组件在 onApplicationEvent 被调用之前已经被创建和注册。
3.2 抽象策略接口
AbstractExecuteStrategy 定义了一个策略执行的基本框架,供具体策略类实现。主要方法有:
mark():默认返回策略标识的字符串,可以被具体策略类重写以返回唯一标识。patternMatchMark():用于模式匹配的标识,允许根据一定模式选择策略。execute(REQUEST requestParam):执行策略的方法,接受一个请求参数,默认实现为空,具体策略类可以重写。executeResp(REQUEST requestParam):执行策略并返回结果的方法,接受请求参数并返回响应,默认实现返回null,具体策略类可以重写。
/*** 策略执行抽象接口**/
public interface AbstractExecuteStrategy<REQUEST, RESPONSE> {/*** 执行策略标识* 用于标识具体的执行策略**/default String mark() {return null;}/*** 执行策略范匹配标识* 用于在多个策略中通过模式匹配选择合适的策略执行**/default String patternMatchMark() {return null;}/*** 执行策略** @param requestParam 执行策略所需的参数,类型为REQUEST*/default void execute(REQUEST requestParam) {}/*** 执行策略,带有返回值** @param requestParam 执行策略所需的参数,类型为REQUEST* @return 执行策略后返回值,类型为RESPONSE*/default RESPONSE executeResp(REQUEST requestParam) {return null;}
}
3.3 上下文
AbstractStrategyChoose 类用于管理和选择具体的策略,并执行对应的策略。主要方法有:
-
choose(String mark, Boolean predicateFlag):如果predicateFlag为true,则进行模式匹配选择策略。如果predicateFlag为false,则直接根据 mark 查询策略。 -
chooseAndExecute(String mark, REQUEST requestParam):根据标识选择策略并执行,调用对应的
execute方法。 -
chooseAndExecute(String mark, REQUEST requestParam, Boolean predicateFlag):根据标识和模式匹配标识选择策略并执行。 -
onApplicationEvent(ApplicationInitializingEvent event):实现ApplicationListener接口,当Spring应用初始化时,自动注册所有的AbstractExecuteStrategy实现类。
3.4 具体策略实现类
@Component
public class AddStrategy implements AbstractExecuteStrategy<Integer, Integer> {@Overridepublic String mark() {return "ADD";}@Overridepublic Integer executeResp(Integer requestParam) {return requestParam + 10; // 假设每次加10}}
3.5 测试
@SpringBootTest
class StrategyPatternApplicationTests {@Autowiredprivate AbstractStrategyChoose strategyChoose;@Testvoid testAddStrategy() {// 测试加法策略Integer addResult = strategyChoose.chooseAndExecuteResp("ADD", 20);System.out.println("Add Strategy Result: " + addResult); // 期望结果:30assertEquals(Integer.valueOf(30), addResult);}
}
参考资料
Java设计模式之策略模式(UML类图分析+代码详解)
12306: 完成高仿铁路 12306 用户 + 抢票 + 订单 + 支付服务
相关文章:
【设计模式】策略模式定义及其实现代码示例
文章目录 一、策略模式1.1 策略模式的定义1.2 策略模式的参与者1.3 策略模式的优点1.4 策略模式的缺点1.5 策略模式的使用场景 二、策略模式简单实现2.1 案例描述2.2 实现代码 三、策略模式的代码优化3.1 优化思路3.2 抽象策略接口3.3 上下文3.4 具体策略实现类3.5 测试 参考资…...
list与iterator的之间的区别,如何用斐波那契数列探索yield
问题 list与iterator的之间的区别是什么?如何用斐波那契数列探索yield? 2 方法 将数据转换成list,通过对list索引和切片操作,以及可以进行添加、删除和修改元素。 iterator是一种对象,用于遍历可迭代对象(如列表、元组…...
抖音店铺数据也就是抖店,如何使用小店数据集来挖掘价值?
抖音商家现在基本达到二百多万家抖店,有一些公司可能会根据开放的数据研究行业分布、GMV等等,就像是也出了专业的一些平台如“蝉妈妈”、“达多多”,对我来说受限制就是难受。 当然也有很多大型合法的数据平台有抖店数据集,但…...
KubeVirt 安装和配置 Windows虚拟机
本文将将介绍如何安装 KubeVirt 和使用 KubeVirt 配置 Windows 虚拟机。 前置条件 准备 Ubuntu 操作系统,一定要安装图形化界面。 安装 Docker(最新版本) 安装 libvirt 和 TigerVNC: apt install libvirt-daemon-system libvir…...
CM API方式设置YARN队列资源
简述 对于CDH版本我们可以参考Fayson的文章,本次是CDP7.1.7 CM7.4.4 ,下面只演示一个设置队列容量百分比的示例,其他请参考cloudera官网。 获取cookies文件 生成cookies.txt文件 curl -i -k -v -c cookies.txt -u admin:admin http://192.168.242.100:7180/api/v44/clusters …...
Mysql常用语法一篇文章速成
文章目录 前言前置环境数据库的增删改查查询数据查询所有条件查询多条件查询模糊查询分页查询排序查询分组查询⭐️⭐️关联查询关联分页查询 添加数据insert插入多条记录不指定列名(适用于所有列都有值的情况) 更新数据更新多条记录更新多个列更新不满足条件的记录 删除统计数…...
Intel nuc x15 重装系统步骤和注意事项(LAPKC71F、LAPKC71E、LAPKC51E)
注意本教程的对象是11代CPU,英伟达独显的nuc x15,不是12代arc显卡的。 x15安装win11 24h2,如果在装系统时联网,windows自动下载的最新驱动有兼容问题,会导致【英特尔显卡控制中心】装不上,或者【英特尔nuc…...
Linux之实战命令59:iwlist应用实例(九十三)
简介: CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布:《Android系统多媒体进阶实战》🚀 优质专栏: Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏: 多媒体系统工程师系列【…...
数据库_SQLite3
下载 1、更新软件源: sudo apt-get update 2、下载SQLite3: sudo apt-get install sqlite3 3、验证: sqlite3启动数据库,出现以下界面代表运行正常。输入 .exit 可以退出数据库 4、安装sqlite3的库 sudo apt-get install l…...
.Net Framework里演示怎么样使用StringBuilder、Math.Min和String.Format
StringBuilder、Math.Min和String.Format, 这几个功能都是我们经常使用的功能, 但是怎么样正确地使用,还是得向微软的开发人员学习。 他们在写.Net Framework的源码时,就会大量使用。 因此,我们可以多看看这分代码,就可以理解他们怎么样使用的。 他们的使用方式,一…...
Oracle创建存储过程,创建定时任务
在Oracle数据库中,创建存储过程和定时任务(也称为调度任务)是常见的数据库管理任务。以下是创建存储过程和定时任务的步骤和说明。 创建存储过程 创建存储过程的sql脚本 create or replace procedure 存储过程名称... is begin脚本逻辑...…...
<HarmonyOS第一课>应用/元服务上架的课后习题
善者,吾善之; 不善者,吾亦善之,德善。 信者,吾信之; 不信者,吾亦信之,德信。 圣人在天下,歙歙焉为天下浑其心,百姓皆注其耳目,圣人皆孩之。 通过&…...
【Python】探索函数的奥秘:从基础到高级的深度解析(下)
目录 🍔 函数的参数进阶 1、函数的参数 2、函数的参数类型(调用) 2.1 位置参数 2.2 关键词参数(Python特有) 3、函数定义时缺省参数(参数默认值) 4、不定长参数 4.1 不定长元组(位置)参数…...
ima.copilot:智慧因你而生
在数字化时代,信息的获取、处理和创作已经成为我们日常工作和学习中不可或缺的一部分。腾讯公司推出的ima.copilot(简称ima)正是为了满足这一需求,它是一款由腾讯混元大模型提供技术支持的智能工作台产品,旨在通过智能…...
Vue-$el属性
原博客地址:深入 Vue.js 的心脏:全面剖析 $el 属性_vue $el-CSDN博客 目录 1 $el是什么 1.1 $el本质 1.2 访问$el时机 1.3 $el与模板的关系 2 $el使用场景 2.1 集成第三方库 2.2 操作DOM元素样式 2.3 处理焦点和事件 2.4 实现自定义指令 3 $e…...
LLC Power Switches and Resonant Tank 笔记
1.概述 上面是一个典型的LLC电路。注意Lm是励磁电感,就是次级线圈空载时的主变压器电感,据说在计算谐振频率时无需关心。然后,作为DCDC电源,它通过调整谐振频率,来改变输出的电流。负载越大,频率越低&#…...
Python 如何在 Web 环境中使用 Matplotlib 进行数据可视化
Python Matplotlib 在 Web 环境中的可视化 数据可视化是数据科学和分析中一个至关重要的部分,它能帮助我们更好地理解和解释数据。在现代应用中,越来越多的开发者希望能够将数据可视化结果展示在网页上。Matplotlib 是 Python 中最常用的数据可视化库之…...
C#-数组:一维数组、二维数组、交错数组
数组:声明初始化过后,就不能在原有的基础上进行 添加 或者 删除 了 一:一维数组 一般将一维数组简称为数组 1.1 数组的声明 int[] arr1; 没有分配房间。初始化后就分配房间了int[] arr2 new int[5]; 存在默认值,为0int[] arr3…...
动态规划应该如何学习?
动态规划如何学习 参考灵神的视频和题解做的笔记(灵神YYDS,以后也都会用这套逻辑去思考) 枚举选哪个: 动态规划入门:从记忆化搜索到递推_哔哩哔哩_bilibili 746. 使用最小花费爬楼梯 - 力扣(LeetCode&a…...
【力扣 + 牛客 | SQL题 | 每日4题】牛客SQL热题210,213,212,219
1. 力扣SQL1076:项目员工2 1.1 题目: 表:Project ---------------------- | Column Name | Type | ---------------------- | project_id | int | | employee_id | int | ---------------------- (project_id, employee_id) 是…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
快刀集(1): 一刀斩断视频片头广告
一刀流:用一个简单脚本,秒杀视频片头广告,还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农,平时写代码之余看看电影、补补片,是再正常不过的事。 电影嘛,要沉浸,…...
【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...
redis和redission的区别
Redis 和 Redisson 是两个密切相关但又本质不同的技术,它们扮演着完全不同的角色: Redis: 内存数据库/数据结构存储 本质: 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能: 提供丰…...
