【软件设计模式】策略模式

1.概念

        策略(Strategy)模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。策略模式属于行为型设计模式,它通过对算法进行封装,把使用算法的责任和算法的实现分割开来,并委派给不同的对象对这些算法进行管理。

 策略模式可以简单理解为:“一件事有多种做法,你可以随时换着来,不用改其他地方”

打个生活比方:
比如你要去上班(这是 “目标”),可以有多种方式(这些方式就是 “策略”):

  • 晴天骑共享单车
  • 下雨打出租车
  • 赶时间就坐地铁

        这些方式都是去上班的办法,互不影响。你可以根据天气、时间随时换,而 “上班” 这个目标本身不用变,换方式时也不用改其他安排。

        核心就是:把做一件事的不同方法单独拎出来,想用哪个就用哪个,切换起来很方便,还不影响其他部分

2.策略模式结构

策略模式包含 3 个核心角色:

  1. 策略接口(Strategy):定义所有支持的算法的公共接口(或抽象类),声明算法的核心方法。
  2. 具体策略(ConcreteStrategy):实现策略接口,包含具体的算法逻辑(如不同的排序算法、支付方式等)。
  3. 上下文(Context):持有一个策略接口的引用,负责调用策略的算法。客户端通过上下文间接使用策略,且上下文可动态切换策略(通过 setter 方法)。

3.优点

  • 灵活性高:算法可动态切换,客户端无需修改代码即可更换策略。
  • 符合开闭原则:新增算法只需新增具体策略类,无需修改上下文或其他策略。
  • 避免多重条件判断:用多态代替if-elseswitch语句,代码更清晰。
  • 算法复用:策略类可在不同场景中复用。

4.适用场景

  • 当一个问题有多种解决方案(算法),且需要动态选择其中一种时(如支付系统的多种支付方式、排序算法的选择)。
  • 当代码中存在大量与算法相关的if-else判断,且这些算法可能频繁变化时。
  • 当需要隐藏算法的具体实现细节,只暴露其接口时。

5.策略模式示例

说明:设计一个 “折扣计算” 模块,支持 3 种折扣策略:

  • 新用户折扣(满 100 减 20)
  • 会员折扣(9 折)
  • 促销折扣(满 200 减 50)
// 1. 策略接口:定义折扣计算方法
public interface DiscountStrategy {// 计算折扣后金额:参数为原价,返回折后价double calculate(double originalPrice);
}// 2. 具体策略:新用户折扣
public class NewUserDiscount implements DiscountStrategy {@Overridepublic double calculate(double originalPrice) {// 满100减20return originalPrice >= 100 ? originalPrice - 20 : originalPrice;}
}// 具体策略:会员折扣(9折)
public class MemberDiscount implements DiscountStrategy {@Overridepublic double calculate(double originalPrice) {return originalPrice * 0.9;}
}// 具体策略:促销折扣(满200减50)
public class PromotionDiscount implements DiscountStrategy {@Overridepublic double calculate(double originalPrice) {return originalPrice >= 200 ? originalPrice - 50 : originalPrice;}
}// 3. 上下文:折扣计算器(使用策略的类)
public class DiscountContext {// 持有策略接口的引用private DiscountStrategy strategy;// 构造方法:初始化时指定策略public DiscountContext(DiscountStrategy strategy) {this.strategy = strategy;}// 动态切换策略public void setStrategy(DiscountStrategy strategy) {this.strategy = strategy;}// 调用策略计算折扣public double getFinalPrice(double originalPrice) {return strategy.calculate(originalPrice);}
}// 1. 策略接口:定义折扣计算方法
public interface DiscountStrategy {// 计算折扣后金额:参数为原价,返回折后价double calculate(double originalPrice);
}// 2. 具体策略:新用户折扣
public class NewUserDiscount implements DiscountStrategy {@Overridepublic double calculate(double originalPrice) {// 满100减20return originalPrice >= 100 ? originalPrice - 20 : originalPrice;}
}// 具体策略:会员折扣(9折)
public class MemberDiscount implements DiscountStrategy {@Overridepublic double calculate(double originalPrice) {return originalPrice * 0.9;}
}// 具体策略:促销折扣(满200减50)
public class PromotionDiscount implements DiscountStrategy {@Overridepublic double calculate(double originalPrice) {return originalPrice >= 200 ? originalPrice - 50 : originalPrice;}
}// 3. 上下文:折扣计算器(使用策略的类)
public class DiscountContext {// 持有策略接口的引用private DiscountStrategy strategy;// 构造方法:初始化时指定策略public DiscountContext(DiscountStrategy strategy) {this.strategy = strategy;}// 动态切换策略public void setStrategy(DiscountStrategy strategy) {this.strategy = strategy;}// 调用策略计算折扣public double getFinalPrice(double originalPrice) {return strategy.calculate(originalPrice);}
}

客户端:

public DiscountStrategy strategy(String role) {switch (role) {case "new":return new NewUserDiscount();case "member":return new MemberDiscount();case "promotion":return new PromotionDiscount();default:throw new IllegalArgumentException("未知用户类型");}}

        可见虽然我们采用策略模式进行算法封装,但是在逻辑分配时还是使用到了if-else式硬编码格式,到后续我们想要新增新的策略也需要修改客户端的代码!

6.策略模式优化

        策略模式的核心是封装算法变化,让客户端可以灵活切换不同实现,避免冗余的条件判断。它与工厂方法模式都通过抽象接口实现解耦,但策略模式聚焦 “行为 / 算法的使用”,工厂方法聚焦 “对象的创建”。实际开发中,两者常结合使用:用工厂方法创建策略对象,用策略模式使用这些对象,既简化了对象创建,又实现了算法的灵活切换。

说明:使用工厂方法+Map集合+策略模式优化

        使用工厂方法可将对象的创建解耦,使用Map集合可消除if-else,使用策略模式可将算法的使用解耦。

/*** 角色处理器接口* 策略模式的核心接口,定义不同角色的处理行为*/
@FunctionalInterface
public interface RoleHandler {/*** 处理用户角色相关业务逻辑* @param userDTO 用户数据传输对象*/void handle(UserDTO userDTO);
}/*** 管理员角色处理器* 处理管理员角色相关的业务逻辑*/
@Component
public class ManagerRoleHandler implements RoleHandler {@Resourceprivate ManagerMapper managerMapper;@Overridepublic void handle(UserDTO userDTO) {managerMapper.insertManager(userDTO);}
}/*** 学生角色处理器* 处理学生角色相关的业务逻辑*/
@Component
public class StudentRoleHandler implements RoleHandler {@Resourceprivate StudentMapper studentMapper;@Overridepublic void handle(UserDTO userDTO) {studentMapper.insertStudent(userDTO);}
}/*** 教师角色处理器* 处理教师角色相关的业务逻辑*/
@Component
public class TeacherRoleHandler implements RoleHandler {@Resourceprivate TeacherMapper teacherMapper;@Overridepublic void handle(UserDTO userDTO) {teacherMapper.insertTeacher(userDTO);}
}/*** 角色处理器工厂* 用于获取不同角色的处理器实例*/
@Component
public class RoleHandlerFactory {private final Map<String, RoleHandler> roleHandlerMap = new HashMap<>();public RoleHandlerFactory(StudentRoleHandler studentRoleHandler, TeacherRoleHandler teacherRoleHandler, ManagerRoleHandler managerRoleHandler) {roleHandlerMap.put(RedisConstant.STUDENT, studentRoleHandler);roleHandlerMap.put(RedisConstant.TEACHER, teacherRoleHandler);roleHandlerMap.put(RedisConstant.MANAGER, managerRoleHandler);}/*** 根据角色名称获取对应的处理器* @param roleName 角色名称* @return 角色处理器* @throws TypeException 当角色不支持时抛出异常*/public RoleHandler getRoleHandler(String roleName) {RoleHandler handler = roleHandlerMap.get(roleName);if (handler == null) {throw new TypeException("不支持的用户角色: " + roleName);}return handler;}
}

客户端使用:

RoleHandler handler = roleHandlerFactory.getRoleHandler(role);
handler.handle(userDTO);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/920102.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/920102.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mac电脑英特尔版本最新系统15.6.1安装php环境

Mac电脑安装php环境 版本环境&#xff1a; 2025-08-22 14:09:19 安装 最新系统15.6.1系统&#xff1a; 新版本的mac不带php环境&#xff0c;需要自己 安装 brew install php8.3 启动说明 查看 . 使用官方方法安装 NVM curl -o- https://raw.githubusercontent.com/nvm-sh/…

Android焦点窗口变化导致遥控键值监听失效问题分析

最近在做语音全局控制Android系统功能&#xff0c;通过集成第三方语音识别sdk得到相关控制指令&#xff0c;然后将指令通过进程间通信传递给当前应用并作出响应。有很多通用指令&#xff0c;比如播放/暂停&#xff0c;Android系统本身就有全局控制指令&#xff1a;KeyEvent.KEY…

降本增效:基于 JavaScript 的 AI 编程 IDE 上下文压缩优化方案

降本增效&#xff1a;基于 JavaScript 的 AI 编程 IDE 上下文压缩优化方案 在当前 AI 辅助编程&#xff08;AI Pair Programming&#xff09;日益普及的背景下&#xff0c;开发者越来越依赖如 GitHub Copilot、Tabnine、CodeLlama 等智能编码工具。然而&#xff0c;一个普遍存在…

DataX HdfsWriter 插件文档

✨博客主页&#xff1a; https://blog.csdn.net/m0_63815035?typeblog &#x1f497;《博客内容》&#xff1a;大数据、Java、测试开发、Python、Android、Go、Node、Android前端小程序等相关领域知识 &#x1f4e2;博客专栏&#xff1a; https://blog.csdn.net/m0_63815035/…

Rancher 管理的 K8S 集群中部署常见应用(MySQL、Redis、RabbitMQ)并支持扩缩容的操作

文章目录一、前提条件二、准备镜像三、进入 Rancher UI 部署3.1. 进入 Workloads3.2. 部署 MySQL3.3. 部署 Redis3.4. 部署 RabbitMQ四、验证服务五、扩缩容&#xff08;UI 操作&#xff09;六、附录yml部署文件6.1. mysql.yaml6.2. redis.yaml6.3. rabbitmq.yaml一、前提条件 …

8.21-8.22网络编程——词典

文章目录一、思维导图二、词典1、服务器2、客户端3、现象三、牛客网刷题一、思维导图 二、词典 1、服务器 #include <myhead.h>#define SER_PORT 8888 //服务器端口号 #define SER_IP "192.168.116.128" //服务器IP地址 //账户密码结构 typedef s…

ffmpeg测试rtsp地址

ffmpeg可以用 ffmpeg 通过 tcp协议/udp协议传输流到 null设备&#xff0c;ffmpeg \-rtsp_transport tcp \ # 使用TCP协议传输RTSP流-timeout 5000000 \ # 设置超时时间为5000000微秒&#xff08;5秒&#xff09;-i "rtsp://admin:admin123192.168.1.…

Apache Commons Math_Java科学计算的利器

1. 引言 1.1 科学计算在现代软件开发中的重要性 随着大数据、人工智能和科学计算需求的不断增长,科学计算能力已成为现代软件开发不可或缺的重要组成部分。从金融风险评估到工程仿真,从数据分析到机器学习,科学计算在各行各业中发挥着关键作用。 科学计算涉及复杂的数学运…

Python爬虫框架设计:类封装与工程化实践​

实战中的UA轮换技巧 import fake_useragent import random class DynamicHeader: def init(self): self.ua_generator fake_useragent.UserAgent() # 注意&#xff1a;实际使用需更新数据路径 self.fingerprints [“chrome125”, “edge115”, “safari17”] # 2025年主流指…

5G 三卡图传终端:公安执法的 “移动可视化指挥中枢”

前言 在公安执法中&#xff0c;“实时取证、高效指挥、安全协同” 是破解现场复杂局面的核心需求。传统执法设备常因传输卡顿、证据存证难、跨警种信息断层等问题影响执法效能&#xff0c;而 5G 便携式多卡高清视频融合终端凭借多网聚合、高清编码、安全存储等特性&#xff0c;…

R语言rbind()和cbind()使用

rbind&#xff1a;r row&#xff08;行&#xff09;cbind&#xff1a;c column&#xff08;列&#xff09; 核心区别方向 • rbind&#xff1a;纵向&#xff08;按行&#xff09;堆叠&#xff0c;行数相加。 • cbind&#xff1a;横向&#xff08;按列&#xff09;拼接&#…

【Linux】开发工具命令指南:深度解析Vim的使用操作

欢迎各位佬进行交流&#xff0c;我们一起无限进步&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 文章目录Vim&#xff1a;多模式高效文本编辑器1. 初次体验 Vim2. Vim 核心工作模式&#xff08;…

【Day 12】73.矩阵置零

文章目录73.矩阵置零题目&#xff1a;思路&#xff1a;方法一&#xff1a;用两个标记数组&#xff08;易理解&#xff0c;额外空间 O(mn)&#xff09;思路&#xff08;直观&#xff09;举例&#xff08;[[1,1,1],[1,0,1],[1,1,1]]&#xff09;优缺点代码实现&#xff08;Go&…

Clustering Enabled Wireless Channel Modeling Using Big Data Algorithms

文章目录Clustering TechniquesPartitioning-Based AlgorithmsDensity-Based AlgorithmsHierarchical-based algorithmsClustering Enabled Channel ModelingCluster-Based Channel ModelsClustering AlgorithmsClustering Techniques 聚类是一种已被广泛用于数据分析的技术。…

基于「多模态大模型 + BGE向量检索增强RAG」的儿童绘画心理健康分析系统(vue+flask+AI算法)

一、项目演示视频 基于「多模态大模型 BGE向量检索增强RAG」的儿童绘画心理健康分析系统(vueflaskAI算法)二、技术栈 前端技术栈 (web-vue) 核心框架: Vue 3.5.13 (Composition API) UI组件库: Element Plus 2.9.4 状态管理: Pinia 2.3.1 路由管理: Vue Router 4.5.0 HTTP客户…

QML中的Component

目录 &#x1f9e0; 核心概念&#xff1a;什么是 Component&#xff1f; &#x1f4ca; Component 的两种主要形式 1. 内联 Component&#xff08;在 QML 文件内部定义&#xff09; 2. 外部 Component&#xff08;单独的 .qml 文件&#xff09; &#x1f3af; Component 的…

什么是模型训练中的 特征提取,如何对光伏发电预测中的特征进行提取

&#x1f50d; 什么是模型训练中的“特征提取” 定义&#xff1a;特征提取是从原始数据中提炼出对预测或分类最有用的信息的过程。它的目标是去掉冗余和噪声&#xff0c;保留能最好反映数据规律的特征。 作用&#xff1a; 降低数据维度&#xff0c;减少计算量 提高模型的泛化…

Linux应急响应一般思路(三)

日志分析Linux日志分析Linux日志类型大致可以分为三类&#xff0c;内核和系统日志、用户日志、应用日志内核和系统日志&#xff1a;这种日志主要由syslog管理、根据其配置文件/etc/syslog.conf中的设置决定内核消息和各种系统程序信息记录到哪个位置用户日志&#xff1a;用户日…

【酒店酒水寄存管理效率低?】佳易王酒水寄存管理系统操作教程全解析

前言&#xff1a; &#xff08;一&#xff09;试用版获取方式 资源下载路径&#xff1a;进入博主头像主页第一篇文章末尾&#xff0c;点击卡片按钮&#xff1b;或访问左上角博客主页&#xff0c;通过右侧按钮获取详细资料。 说明&#xff1a;下载文件为压缩包&#xff0c;使用…

Unity 套圈捕捉 UI 实现分享:椭圆环 Shader + 动态进度

Unity 套圈捕捉 UI 实现分享 期望表现效果 《拼贴冒险传 / PatchQuest》 捕捉进度 动态UI实现效果 目标&#xff1a;角色 A 套圈怪物 B&#xff0c;进度环显示围绕角度。技术点&#xff1a;Shader 绘制椭圆环&#xff0c;支持描边、顺/逆时针,需要对两个切口也进行描边。 技术…