设计模式探索:策略模式
1. 什么是策略模式(Strategy Pattern)
定义
策略模式(Strategy Pattern)的原始定义是:定义一系列算法,将每一个算法封装起来,并使它们可以相互替换。策略模式让算法可以独立于使用它的客户端而变化。
目的
策略模式的目的是在软件开发中,当实现某一个功能存在多种算法或者策略时,可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能。
比如网购,你可以选择工商银行、农业银行、建设银行等等,但是它们提供的算法都是一致的,就是帮你付款。

角色
策略模式的主要角色如下:
- 抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
- 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。
- 环境或上下文(Context)类:是使用算法的角色,持有一个策略类的引用,最终给客户端调用。
UML类图
在策略模式中可以定义一些独立的类来封装不同的算法,每一个类封装一种具体的算法,在这里每一个封装算法的类都可以被称为一种策略,为了保证这些策略在使用时具有一致性,一般会提供一个抽象的策略类来做算法的声明.而每种算法对应一个具体的策略类。

实现代码
// 抽象策略类
public interface Strategy {void algorithm();
}// 具体策略类A
public class ConcreteStrategyA implements Strategy {@Overridepublic void algorithm() {System.out.println("执行策略A");}
}// 具体策略类B
public class ConcreteStrategyB implements Strategy {@Overridepublic void algorithm() {System.out.println("执行策略B");}
}// 环境类
public class Context {// 维持一个对抽象策略类的引用private Strategy strategy;public Context(Strategy strategy) {this.strategy = strategy;}// 调用策略类中的算法public void algorithm() {strategy.algorithm();}
}// 客户端代码
public class Client {public static void main(String[] args) {Strategy strategyA = new ConcreteStrategyA();Context context = new Context(strategyA); // 可以在运行时指定类型context.algorithm();}
}
2.优缺点
优点
- 易于扩展和维护:由于不同的算法被封装在不同的类中,所以我们可以很容易地添加新的算法或修改已有算法,而不需要修改客户端的代码。
- 提高代码的可读性:将不同的算法封装在不同的类中,使得代码更加模块化,易于理解和维护。
- 消除大量的条件语句:使用策略模式,我们可以将不同的算法替换成不同的类,从而消除大量的if-else语句,使得代码更加简洁和易于理解。
缺点
- 需要额外的类和接口:使用策略模式,我们需要为每个算法都创建一个独立的类,从而增加了代码的复杂度。
- 客户端需要知道所有的策略类:使用策略模式,客户端需要知道所有的策略类,以便在运行时选择合适的策略。这可能会增加代码的复杂度。
应用场景
策略模式适用于以下场景:
- 需要根据不同的条件选择不同的算法时:例如,计算器程序需要根据用户输入的运算符选择相应的计算方法。
- 需要在运行时动态地选择算法时:例如,某个系统需要根据用户的配置或环境变量来选择合适的算法。
- 需要将算法的实现细节与客户端代码分离时:例如,某个系统需要根据不同的数据来源来解析数据,但是客户端并不关心数据的解析细节。
总结
策略模式通过定义一系列算法,将每一个算法封装起来,并使它们可以相互替换,从而让算法可以独立于使用它的客户端而变化。通过使用策略模式,可以提高代码的扩展性、可读性和维护性,同时也可以消除大量的条件语句。
在工作中,为了消除代码中的大量 if-else 语句并提升代码的可维护性和扩展性,可以使用策略模式。下面是详细的实现步骤和代码示例。
3.如何用设计模式消除代码中的ifelse(你在工作中使用过哪些设计模式)
不使用设计模式
这是一个请假审批流程的代码示例,包含员工类、请假单类和审核类,直接使用 if-else 语句来处理不同的审批规则。
public class Employee {private String name; // 姓名private int level; // 级别: P6, P7, P8// Constructor, getters and setters
}public class LeaveForm {private Employee employee; // 员工private String reason; // 请假原因private int days; // 天数private int type; // 类型: 0-病假, 1-婚丧假, 2-年假// Constructor, getters and setters
}public class LeaveService {public void audit(LeaveForm leaveForm) {// 3天以下婚丧假, 自动通过if (leaveForm.getDays() <= 3 && leaveForm.getType() == 1) {System.out.println("三天以下婚丧假 无需审批自动通过!");}// 3天以上婚丧假else if (leaveForm.getDays() > 3 && leaveForm.getType() == 1) {System.out.println("三天以上婚丧假 进入上级审批流程!");}// 总经理请假else if (leaveForm.getEmployee().getLevel() == 9) {System.out.println("总经理请假无需审批自动通过!");}// 一天病假else if (leaveForm.getDays() == 1 && leaveForm.getType() == 0) {System.out.println("一天病假无需审批自动通过!");}// 一天以上病假else if (leaveForm.getDays() > 1 && leaveForm.getType() == 0) {System.out.println("一天以上病假进入审批流程!");}}
}
使用策略模式进行优化
通过策略模式,将所有的 if-else 分支的业务逻辑抽取为各种策略类,判断条件和执行逻辑封装到对应的策略类中,让客户端去依赖策略接口,保证具体策略类的改变不影响客户端。
策略接口
public interface AuditStrategy {boolean isSupport(LeaveForm leaveForm);void audit(LeaveForm leaveForm);int getPriority();String getName();
}
具体策略类
public class AuditStrategyImpl_1 implements AuditStrategy {@Overridepublic boolean isSupport(LeaveForm leaveForm) {return leaveForm.getDays() <= 3 && leaveForm.getType() == 1;}@Overridepublic void audit(LeaveForm leaveForm) {System.out.println(leaveForm);System.out.println("三天以下婚丧假 无需审批自动通过!");}@Overridepublic int getPriority() {return 0;}@Overridepublic String getName() {return "三天以下婚假审批规则";}
}public class AuditStrategyImpl_2 implements AuditStrategy {@Overridepublic boolean isSupport(LeaveForm leaveForm) {return leaveForm.getDays() > 3 && leaveForm.getType() == 1;}@Overridepublic void audit(LeaveForm leaveForm) {System.out.println(leaveForm);System.out.println("三天以上婚丧假 进入上级审批流程!");}@Overridepublic int getPriority() {return 0;}@Overridepublic String getName() {return "三天以上婚丧假审批规则";}
}public class AuditStrategyImpl_3 implements AuditStrategy {@Overridepublic boolean isSupport(LeaveForm leaveForm) {return leaveForm.getEmployee().getLevel() == 9;}@Overridepublic void audit(LeaveForm leaveForm) {System.out.println(leaveForm);System.out.println("总经理请假无需审批自动通过!");}@Overridepublic int getPriority() {return 999;}@Overridepublic String getName() {return "总经理请假审批规则";}
}
策略工厂
public class AuditStrategyFactory {private final static AuditStrategyFactory factory = new AuditStrategyFactory();private List<AuditStrategy> auditStrategyList = new ArrayList<>();private AuditStrategyFactory() {auditStrategyList.add(new AuditStrategyImpl_1());auditStrategyList.add(new AuditStrategyImpl_2());auditStrategyList.add(new AuditStrategyImpl_3());// Add more strategies here}public static AuditStrategyFactory getInstance() {return factory;}public AuditStrategy getAuditStrategy(LeaveForm leaveForm) {AuditStrategy auditStrategy = null;for (AuditStrategy strategy : auditStrategyList) {if (strategy.isSupport(leaveForm)) {if (auditStrategy == null || strategy.getPriority() > auditStrategy.getPriority()) {auditStrategy = strategy;}}}if (auditStrategy == null) {throw new RuntimeException("没有匹配到请假审核规则");}return auditStrategy;}
}
业务类
public class LeaveServiceNew {public void audit(LeaveForm leaveForm) {AuditStrategyFactory factory = AuditStrategyFactory.getInstance();AuditStrategy strategy = factory.getAuditStrategy(leaveForm);strategy.audit(leaveForm);}
}
测试
public class Client {public static void main(String[] args) {LeaveServiceNew leaveServiceNew = new LeaveServiceNew();LeaveForm form1 = new LeaveForm(new Employee("李总经理", 9), "甲流发烧", 10, 0);leaveServiceNew.audit(form1);LeaveForm form2 = new LeaveForm(new Employee("打工人1", 2), "甲流发烧", 2, 0);leaveServiceNew.audit(form2);LeaveForm form3 = new LeaveForm(new Employee("打工人2", 3), "结婚", 2, 1);leaveServiceNew.audit(form3);LeaveForm form4 = new LeaveForm(new Employee("打工人3", 4), "请年假,休息休息", 5, 2);leaveServiceNew.audit(form4);}
}
添加新规则
如果需要添加新的年假规则,只需要创建新的策略类并添加到工厂中即可。
public class AuditStrategyImpl_6 implements AuditStrategy {@Overridepublic boolean isSupport(LeaveForm leaveForm) {return leaveForm.getType() == 2;}@Overridepublic void audit(LeaveForm leaveForm) {System.out.println(leaveForm);System.out.println("查询您的剩余年假天数...");System.out.println("剩余年假还有6天, 进入审批流程");}@Overridepublic int getPriority() {return 0;}@Overridepublic String getName() {return "年假审批规则";}
}
在工厂类中添加新的策略:
private AuditStrategyFactory() {auditStrategyList.add(new AuditStrategyImpl_1());auditStrategyList.add(new AuditStrategyImpl_2());auditStrategyList.add(new AuditStrategyImpl_3());auditStrategyList.add(new AuditStrategyImpl_6()); // 新添加的年假规则// Add more strategies here
}
通过这种方式,已经成功消除了 if-else 结构,每当新来了一种请假规则,只需要添加新的规则处理策略,并修改工厂中的集合。如果要使得程序符合开闭原则,可以通过反射机制,动态地加载策略类。
使用反射机制动态加载策略类
public class AuditStrategyFactory {private final static AuditStrategyFactory factory = new AuditStrategyFactory();private List<AuditStrategy> auditStrategyList = new ArrayList<>();private AuditStrategyFactory() {// 动态加载策略类try {String packageName = "com.example.strategies"; // 策略类所在包名ClassLoader classLoader = Thread.currentThread().getContextClassLoader();String path = packageName.replace('.', '/');Enumeration<URL> resources = classLoader.getResources(path);List<File> dirs = new ArrayList<>();while (resources.hasMoreElements()) {URL resource = resources.nextElement();dirs.add(new File(resource.getFile()));}for (File directory : dirs) {auditStrategyList.addAll(findClasses(directory, packageName));}} catch (Exception e) {e.printStackTrace();}}private List<AuditStrategy> findClasses(File directory, String packageName) throws ClassNotFoundException, InstantiationException, IllegalAccessException {List<AuditStrategy> strategies = new ArrayList<>();if (!directory.exists()) {return strategies;}File[] files = directory.listFiles();for (File file : files) {if (file.isDirectory()) {strategies.addAll(findClasses(file, packageName + "." + file.getName()));} else if (file.getName().endsWith(".class")) {String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);Class<?> clazz = Class.forName(className);if (AuditStrategy.class.isAssignableFrom(clazz) && !Modifier.isAbstract(clazz.getModifiers())) {strategies.add((AuditStrategy) clazz.newInstance());}}}return strategies;}public static AuditStrategyFactory getInstance() {return factory;}public AuditStrategy getAuditStrategy(LeaveForm leaveForm) {AuditStrategy auditStrategy = null;for (AuditStrategy strategy : auditStrategyList) {if (strategy.isSupport(leaveForm)) {if (auditStrategy == null || strategy.getPriority() > auditStrategy.getPriority()) {auditStrategy = strategy;}}}if (auditStrategy == null) {throw new RuntimeException("没有匹配到请假审核规则");}return auditStrategy;}
}
通过这种方式,策略类可以动态地从指定包中加载,实现了真正的开闭原则。
相关文章:
设计模式探索:策略模式
1. 什么是策略模式(Strategy Pattern) 定义 策略模式(Strategy Pattern)的原始定义是:定义一系列算法,将每一个算法封装起来,并使它们可以相互替换。策略模式让算法可以独立于使用它的客户端而…...
提升效能:Symfony 性能优化实用指南
Symfony 是一个功能丰富的 PHP Web 框架,但在构建高性能应用程序时,开发者需要考虑多种性能优化策略。本文将探讨一系列实用的 Symfony 性能优化技巧,帮助开发者提高应用程序的响应速度和整体性能。 1. 了解 Symfony 缓存机制 Symfony 提供…...
1.pwn的汇编基础(提及第一个溢出:整数溢出)
汇编掌握程度 能看懂就行,绝大多数情况不需要真正的编程(shellcode题除外) 其实有时候也不需要读汇编,ida F5 通常都是分析gadget,知道怎么用, 调试程序也不需要分析每一条汇编指令,单步执行然后查看寄存器状态即可 但…...
迎接AI新时代:GPT-5即将登场的巨大变革与应用前瞻
迎接AI新时代:GPT-5即将登场的巨大变革与应用前瞻 💎1. GPT-5 一年半后发布:AI新时代的来临1.1 GPT-5的飞跃:从高中生到博士生 💎2. GPT-5的潜在应用场景💎2.1 医疗诊断和健康管理💎2.2 教育领域…...
封锁-封锁模式(共享锁、排他锁)、封锁协议(两阶段封锁协议)
一、引言 1、封锁技术是目前大多数商用DBMS采用的并发控制技术,封锁技术通过在数据库对象上维护锁来实现并发事务非串行调度的冲突可串行化 2、基于锁的并发控制的基本思想是: 当一个事务对需要访问的数据库对象,例如关系、元组等进行操作…...
跨境干货|最新注册Google账号方法分享
谷歌账号对做跨境外贸业务的人来说是刚需,目前来说大部分的海外社媒平台、工具都可以用谷歌账号来注册。但是仍然有很多朋友并不知道如何注册这个谷歌账号,今天就来给大家分享2个注册谷歌账号的方法,一个是手机号注册,一个是如何跳…...
MySQL第三天作业
一、在数据库中创建一个表student,用于存储学生信息 CREATE TABLE student( id INT PRIMARY KEY, name VARCHAR(20) NOT NULL, grade FLOAT ); 1、向student表中添加一条新记录 记录中id字段的值为1,name字段的值为"monkey"…...
网络安全应急处理流程
网络安全应急处理流程是指在发生网络安全事件时,组织应采取的一系列措施,以快速响应、控制、恢复和调查网络安全事件,确保业务连续性和数据安全。以下是一个详细的网络安全应急处理流程: 1. 准备阶段 目标:建立和维护…...
昇思25天学习打卡营第12天 | LLM原理和实践:MindNLP ChatGLM-6B StreamChat
1. MindNLP ChatGLM-6B StreamChat 本案例基于MindNLP和ChatGLM-6B实现一个聊天应用。 ChatGLM-6B应该是国内第一个发布的可以在消费级显卡上进行推理部署的国产开源大模型,2023年3月就发布了。我在23年6月份的时候就在自己的笔记本电脑上部署测试过,当…...
中英双语介绍加拿大多伦多(Toronto)
中文版 多伦多概述 多伦多(Toronto)是加拿大最大的城市,也是北美地区重要的经济、文化和金融中心。以下是对多伦多的详细介绍,包括其经济地位、金融中心、人口、地理位置、高等教育、移民政策、著名景点和居住的名人等方面的信息…...
【YOLOv9教程】如何使用YOLOv9进行图像与视频检测
《博主简介》 小伙伴们好,我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源,可关注公-仲-hao:【阿旭算法与机器学习】,共同学习交流~ 👍感谢小伙伴们点赞、关注! 《------往期经典推…...
Text2SQL提问中包括时间的实战方案
大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…...
点胶系统实战1-项目介绍
准备实战开发如下图的多轴点胶系统实战课程,内容设计界面开发、运动板块开发、任务管理、点胶的控制等。我们将和进入这个领域的初学者门一起进步。 有感兴趣的小伙伴,可以关注点赞,或评论区反馈你们的重点关注的内容,那些部分我…...
【MYSQL】InnoDB引擎为什么选可重复读作为默认隔离级别
InnoDB引擎为什么选可重复读作为默认隔离级别 一般的DBMS系统,默认都会使用读提交(Read-Comitted,RC)作为默认隔离级别,如Oracle、SQL Server等,而MySQL却使用可重复读(Read-Repeatable&#x…...
数据列表组件-报表
当数据在后端接口查询到,需要在页面展示出来,如果项目有很多report ,可以把列表做一个组件 效果如下: js代码: <!DOCTYPE html> <html> <head><meta charset"utf-8" /><title&g…...
基于Android Studio订餐管理项目
目录 项目介绍 图片展示 运行环境 获取方式 项目介绍 能够实现登录,注册、首页、订餐、购物车,我的。 用户注册后,登陆客户端即可完成订餐、浏览菜谱等功能,点餐,加入购物车,结算,以及删减…...
华为OSPF配置DR和BDR与指定DR
基础配置 <Huawei>sys #进入配置模式 Enter system view, return user view with CtrlZ. [Huawei]un in en #关闭报文弹窗 Info: Information center is disabled. [Huawei]sys R1 #设备名更改为R1 [R1]int g0/0/0 …...
【学习笔记】程序设计竞赛
程序设计竞赛 文章目录 程序设计竞赛0x00 基本操作指南0x01 算法分析0x02 STL和基本数据结构栈队列集合map 0x03 排序插入排序归并排序(Merge Sort)快速排序 0x04 搜索技术BFSDFS回溯与剪枝 深度迭代ID A*A star双向广搜 0x05 递推方程0x06 高级数据结构并查集二叉树…...
11-云服务器处理单细胞转录组数据
目录 安装R及相关包 安装 shiny 进行安装包 安装BiocManager 安装Seurat包 网页端Rstudio需打开8787端口 Ubuntu上安装R包linux库缺失 关于服务器配置及相关处理可见:linux学习笔记_hx2024的博客-CSDN博客 安装R及相关包 8-阿里云服务器 ECS配置R及Studio Server-CS…...
vs+qt5.0 使用poppler-qt5 操作库获取pdf所有文本输出到txt操作
先获取poppler库,编译出lib与dll,配置好依赖环境,获取某页所有文本: QList<QString> PDFkitEngine::GetText(int nPageNum) { QList<QString> lstText; Poppler::Page* pPage NULL; pPage GetPage(nPageNu…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
页面渲染流程与性能优化
页面渲染流程与性能优化详解(完整版) 一、现代浏览器渲染流程(详细说明) 1. 构建DOM树 浏览器接收到HTML文档后,会逐步解析并构建DOM(Document Object Model)树。具体过程如下: (…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
GitHub 趋势日报 (2025年06月06日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...
用鸿蒙HarmonyOS5实现中国象棋小游戏的过程
下面是一个基于鸿蒙OS (HarmonyOS) 的中国象棋小游戏的实现代码。这个实现使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chinesechess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├──…...
xmind转换为markdown
文章目录 解锁思维导图新姿势:将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件(ZIP处理)2.解析JSON数据结构3:递归转换树形结构4:Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...
