设计模式——行为型模式——策略模式(含实际业务使用示例、可拷贝直接运行)
目录
策略模式
定义
组成和UML图
代码示例
实际业务场景下策略模式的使用
策略模式优缺点
使用场景
JDK中使用策略模式示例
参考文档
策略模式
定义
策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。
策略模式属于对象行为模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。
考虑如下场景:去旅游选择出行方式时,可以骑自行车、坐汽车、坐火车、坐飞机,在不考虑使用策略模式的时候使用if elseif else语句进行判断,此时代码中存在大量条件判断语句,代码不便于增强,不符合开闭原则。将其修改为策略模式可以去掉代码中大量的if判断语句,同时增强代码的可扩展性。

组成和UML图
策略模式的组成
-  抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。 
-  具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。 
-  环境(Context)类:持有一个策略类的引用,最终给客户端调用。 
策略模式的UML图

代码示例
促销活动:一家百货公司在定年度的促销活动。针对不同的节日(春节、中秋节、圣诞节)推出不同的促销活动,由促销员将促销活动展示给客户。类图如下:

代码实现:以下示例代码仅仅是一个示例,便于理解策略模式,根据我的实习经验具体公司代码中策略模式不会这样使用的。【最近时间太紧,后期会梳理一下在实习中碰到的公司代码使用策略模式的例子,并更新以下代码示例(2024-05-08已更新,见下文)】
package com.scut.adapter.strategy;// 定义抽象策略类
public interface Strategy {void show();
}package com.scut.adapter.strategy;
// 策略实现类A
public class StrategyA implements Strategy{@Overridepublic void show() {System.out.println("中秋节活动,赠送中秋大礼包...");}
}package com.scut.adapter.strategy;// 策略实现类B
public class StrategyB implements Strategy{@Overridepublic void show() {System.out.println("充值一百,送二百...");}
}package com.scut.adapter.strategy;// 策略实现类C
public class StrategyC implements Strategy{@Overridepublic void show() {System.out.println("圣诞节活动,赠送水果大礼盒一套...");}
}package com.scut.adapter.strategy;// 定义售卖员,策略上下文类
public class SaleMan {private Strategy strategy;public SaleMan(Strategy strategy) {this.strategy = strategy;}public Strategy getStrategy() {return strategy;}public void setStrategy(Strategy strategy) {this.strategy = strategy;}public void saleShow(){strategy.show();}
}package com.scut.adapter.strategy;// 定义客户端、测试类
public class Client {public static void main(String[] args) {SaleMan saleMan = new SaleMan(new StrategyA());saleMan.saleShow();System.out.println("====================");saleMan.setStrategy(new StrategyB());saleMan.saleShow();System.out.println("====================");}
}实际业务场景下策略模式的使用
前置说明:公司中策略模式的使用不会按照以上代码示例的方式使用,更多是结合@Autowire注解注入一系列接口实现类构成一个List集合,进而遍历List集合中的实现类选择具体方法。如下:

模拟业务场景:电商业务通常需要和发票进行交互,发票简单状态包括:开票、改票、退票。将其抽象并结合策略模式进行设计可以有效减少代码中if else条件判断语句的数量并提高代码可读性。
代码结构如下:

具体代码如下:
package com.example.webmodule;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/hello")
public class InvoiceController {@Autowiredprivate InvoiceService invoiceService;@GetMapping("")public String testInvoice(int invoiceStatus){invoiceService.invoiceTest(invoiceStatus);return "OK";}
}package com.example.webmodule;public interface InvoiceHandle {// 实现类选择器boolean canHandle(int invoiceStatus);// 具体业务实现void invoiceProcess();
}package com.example.webmodule;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;@Service
@Slf4j
public class InvoiceService {@AutowiredList<InvoiceHandle> invoiceHandleList;public void invoiceTest(int invoiceStatus){for (InvoiceHandle invoiceHandle : invoiceHandleList) {if(invoiceHandle.canHandle(invoiceStatus)){invoiceHandle.invoiceProcess();}}}
}package com.example.webmodule;public enum InvoiceStatusEnum {MAKE_INVOICE(1, "开票"),UPDATE_INVOICE(2, "改票"),REFUND_INVOICE(3,"退票");private int code;private String value;InvoiceStatusEnum(int code, String value) {this.code = code;this.value = value;}public int getCode(){return code;}public String getValue(){return value;}public void setCode(int code){this.code = code;}public void setValue(String value){this.value = value;}
}package com.example.webmodule;import org.springframework.stereotype.Service;/*** 开票*/
@Service
public class MakeInvoiceImpl implements InvoiceHandle{@Overridepublic boolean canHandle(int invoiceStatus) {return InvoiceStatusEnum.MAKE_INVOICE.getCode() == invoiceStatus;}@Overridepublic void invoiceProcess() {System.out.println("正在开票...");}
}package com.example.webmodule;import org.springframework.stereotype.Service;/*** 退票*/
@Service
public class RefundInvoiceImpl implements InvoiceHandle{@Overridepublic boolean canHandle(int invoiceStatus) {return InvoiceStatusEnum.REFUND_INVOICE.getCode() == invoiceStatus;}@Overridepublic void invoiceProcess() {System.out.println("正在退票...");}
}package com.example.webmodule;import org.springframework.stereotype.Service;/*** 改票*/
@Service
public class UpdateInvoiceImpl implements InvoiceHandle{@Overridepublic boolean canHandle(int invoiceStatus) {return InvoiceStatusEnum.UPDATE_INVOICE.getCode() == invoiceStatus;}@Overridepublic void invoiceProcess() {System.out.println("正在改票...");}
}代码演示效果如下:



策略模式优缺点
优点:
-  策略类之间可以自由切换 由于策略类都实现同一个接口,所以使它们之间可以自由切换。 
-  易于扩展 增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码,符合“开闭原则“ 
-  避免使用多重条件选择语句(if else),充分体现面向对象设计思想。 
缺点:
-  客户端必须知道所有的策略类,并自行决定使用哪一个策略类。 
-  策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量。 
使用场景
-  一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。 
-  一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。 
-  系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。 
-  系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。 
-  多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。 
JDK中使用策略模式示例
        在Arrays类中的 sort() 方法中的Comparator使用的就是策略模式,具体源码可以自行查找以下。
参考文档
策略模式介绍及其具体使用场景_策略模式应用场景-CSDN博客
相关文章:
 
设计模式——行为型模式——策略模式(含实际业务使用示例、可拷贝直接运行)
目录 策略模式 定义 组成和UML图 代码示例 实际业务场景下策略模式的使用 策略模式优缺点 使用场景 JDK中使用策略模式示例 参考文档 策略模式 定义 策略模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化…...
Rust:foo(x)、foo(x),还是foo(x.clone())?
一、一个实际问题 用一个线性代数库的求逆矩阵函数时,让我很不爽,我必须按照下面的形式写调用代码: ...if let Some(inv_mat) try_inverse(mat.clone()) {...}...注意 try_inverse 函数的参数传递形式,函数参数是 mat.clone() 而…...
 
「JavaEE」多线程案例1:单例模式阻塞队列
🎇个人主页:Ice_Sugar_7 🎇所属专栏:JavaEE 🎇欢迎点赞收藏加关注哦! 多线程案例分析 🍉单例模式🍌饿汉模式🍌懒汉模式🍌指令重排序 🍉阻塞队列&a…...
 
pdf2htmlEX:pdf 转 html,医学指南精细化处理第一步
pdf2htmlEX:pdf 转 html,医学指南精细化处理第一步 单文件转换多文件转换 代码:https://github.com/coolwanglu/pdf2htmlEX 拉取pdf2htmlEX 的 Docker: docker pull bwits/pdf2htmlex # 拉取 bwits/pdf2htmlex不用进入容器&…...
 
【webrtc】MessageHandler 6: 基于线程的消息处理:StunRequest实现包发送和超时重传
G:\CDN\rtcCli\m98\src\p2p\base\stun_request.cc使用OnMessage 实现包的发送和包的超时重传StunRequest 一个StunRequest 代表是一个独立的请求的发送STUN消息 要不是发送前构造好的,要不就是按照需要构建的使用StunRequestManager: 每一个STUNRequest 携带一个交互id 写入m…...
 
《Python编程从入门到实践》day22
# 昨日知识点回顾 方法重构、驾驶飞船左右移动、全屏显示 飞船不移动解决,问题出在移动变量x更新 # Ship.pysnipdef update(self):"""根据移动标志调整飞船的位置"""# 更新飞船而不是rect对象的x值# 如果飞船右移的标志和飞船外接…...
 
介绍 ffmpeg.dll 文件以及ffmpeg.dll丢失怎么办的五种修复方法
ffmpeg.dll 是一个动态链接库文件,属于 FFmpeg运行库。它在计算机上扮演着非常重要的角色,因为它提供了许多应用程序和操作系统所需的功能和组件。当 ffmpeg.dll 文件丢失或损坏时,可能会导致程序无法正常运行,甚至系统崩溃。下面…...
 
AI换脸原理(6)——人脸分割介绍
一、介绍 人脸分割是计算机视觉和图像处理领域的一项重要任务,它主要涉及到将图像中的人脸区域从背景或其他非人脸区域中分离出来。这一技术具有广泛的应用场景,如人脸识别、图像编辑、虚拟背景替换等。 在计算机视觉(CV)领域,经典的分割技术可以主要划分为三类:语义分…...
【C++并发编程】(二)线程的创建、分离和连接
文章目录 (二)线程的创建、分离和链接创建线程:示例线程的分离(detach)和连接(join) (二)线程的创建、分离和链接 创建线程:示例 线程(Thread&a…...
 
利用生成式AI重新构想ITSM的未来
对注入 AI 的生成式 ITSM 的需求,在 2023 年 Gartner AI 炒作周期中,生成式 AI 达到预期值达到顶峰后,三分之二的企业已经将生成式 AI 集成到其流程中。 你问为什么这种追求?在预定义算法的驱动下,IT 服务交付和管理中…...
 
完美解决AttributeError: module ‘backend_interagg‘ has no attribute ‘FigureCanvas‘
遇到这种错误通常是因为matplotlib的后端配置问题。在某些环境中,尤其是在某些特定的IDE或Jupyter Notebook环境中,可能会因为后端配置不正确而导致错误。错误信息提示 module backend_interagg has no attribute FigureCanvas 意味着当前matplotlib的后…...
CMakeLists.txt语法规则:条件判断中表达式说明一
一. 简介 前面学习了 CMakeLists.txt语法中的 部分常用命令,常量变量,双引号的使用。 前面一篇文章也简单了解了 CMakeLists.txt语法中的条件判断,文章如下: CMakeLists.txt语法规则:条件判断说明一-CSDN博客 本文…...
 
《QT实用小工具·五十三》会跑走的按钮
1、概述 源码放在文章末尾 该项目实现了会逃跑的按钮: 两个按钮,一个为普通按钮,另一个为会跑走的按钮 鼠标移到上面时,立刻跑掉 针对鼠标、键盘、触屏进行优化 随机交换两个按钮的文字、偶尔钻到另一个按钮下面、鼠标移开自…...
Servlet的几种用法?
serlet 1.定义:Serlet是使用Java编写的运行在服务器端的程序 2.Servlet主要是用于处理浏览器端发送的Http请求,并返回一个响应 3.Servlet开发需要使用到的包: java.servlet java.servlet.http 一.Servlet注册 1.xml方式 <servlet>…...
 
Golang | Leetcode Golang题解之第69题x的平方根
题目: 题解: func mySqrt(x int) int {if x 0 {return 0}C, x0 : float64(x), float64(x)for {xi : 0.5 * (x0 C/x0)if math.Abs(x0 - xi) < 1e-7 {break}x0 xi}return int(x0) }...
 
AR人脸美妆SDK解决方案,让妆容更加贴合个人风格
美妆行业正迎来前所未有的变革,为满足企业对高效、精准、创新的美妆技术需求,美摄科技倾力打造了一款企业级AR人脸美妆SDK解决方案,为企业打开美妆领域的新世界大门。 革命性的人脸美妆技术 美摄科技的AR人脸美妆SDK解决方案,不…...
 
Python-100-Days: Day09 Object-oriented programming(OOP) Upgrade
1.property装饰器 之前有讨论过, Python中属性和方法访问权限的问题,不建议将属性设置为私有的,倘若直接将属性暴露给外界也是存在问题的。例如,我们没有办法检查赋给属性的值是否有效。之前的建议是将属性命名以单下划线开头&am…...
 
虹科Pico汽车示波器 | 免拆诊断案例 | 2010款凯迪拉克SRX车发动机无法起动
故障现象 一辆2010款凯迪拉克SRX车,搭载LF1发动机,累计行驶里程约为14.3万km。该车因正时链条断裂导致气门顶弯,大修发动机后试车,起动机运转有力,但发动机没有着机迹象;多起动几次,火花塞会变…...
 
ECC 号码总结
1、问题背景 在手机开发过程中,经常遇见各种紧急号码问题,在此特意总结下紧急号码相关知识。 2、紧急号码来源 在MTK RILD EccNumberSource.h中,定义了如下几种紧急号码来源。 按优先级排序介绍如下 2.1、SOURCE_NETWORK 网络下发ÿ…...
 
《大疆二次开发》EMQX和MQTT部署
EMQX 服务器 基础知识 概念 EMQX (Erlang/Enterprise/Elastic MQTT Broker) ;EMQ/EMQX就是MQTT Broker的一种实现;一款开源的大规模分布式 MQTT 消息服务器,功能丰富,专为物联网和实时通信应用而设计;支持多种协议&…...
 
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
 
YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
 
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
 
NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...
