在软件开发中,我们经常会遇到需要根据不同情况选择不同算法或行为的场景。传统的做法可能是使用大量的条件语句(if-else或switch-case),但随着需求的增加和变化,这种硬编码的方式会导致代码难以维护和扩展。策略模式(Strategy Pattern)正是为了解决这类问题而诞生的一种优雅的设计模式。

策略模式属于行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。这种模式让算法的变化独立于使用算法的客户端,从而实现了算法的灵活切换和扩展。

策略模式的核心概念

1. 模式结构

策略模式由三个核心组件构成:

  1. 策略接口(Strategy Interface):定义所有支持的算法或行为的公共接口。这是各种具体策略的抽象,确保了策略之间的互换性。

  2. 具体策略类(Concrete Strategies):实现策略接口的具体算法类。每个具体策略类都提供了算法接口的不同实现。

  3. 上下文类(Context):持有一个策略对象的引用,并通过策略接口与具体策略交互。上下文不直接实现算法,而是委托给当前策略对象。

2. UML类图

+-------------------+       +---------------------+
|    Context        |       |   <<Interface>>     |
|-------------------|       |    Strategy         |
| -strategy:Strategy|<>---->|---------------------|
|-------------------|       | +executeAlgorithm() |
| +setStrategy()    |       +---------------------+
| +execute()        |               ^
+-------------------+               |+----------+----------+|                     |+----------------+   +----------------+| ConcreteStrategyA | | ConcreteStrategyB ||------------------| |------------------|| +executeAlgorithm()| +executeAlgorithm()|+----------------+   +----------------+

策略模式的深入解析

1. 模式动机

在软件开发中,我们经常会遇到需要根据不同条件执行不同算法的情况。例如:

  • 支付系统支持多种支付方式(信用卡、PayPal、支付宝等)

  • 导航系统提供多种路线计算策略(最快路线、最短路线、避开收费路线等)

  • 数据压缩工具支持多种压缩算法(ZIP、RAR、7z等)

如果直接在业务代码中使用条件语句来处理这些不同的算法选择,会导致以下问题:

  1. 违反开闭原则:新增或修改算法需要修改现有代码

  2. 代码臃肿:随着算法数量增加,条件判断会变得复杂

  3. 难以维护:算法实现与业务逻辑耦合在一起

  4. 复用困难:相同的算法难以在不同的上下文中复用

策略模式通过将算法封装为独立的策略类,完美解决了上述问题。

2. 模式实现

让我们通过一个更完整的电商系统支付示例来深入理解策略模式的实现。

支付策略示例

// 策略接口
public interface PaymentStrategy {void pay(double amount);boolean validatePaymentDetails();
}// 具体策略类:信用卡支付
public class CreditCardStrategy implements PaymentStrategy {private String name;private String cardNumber;private String cvv;private String expiryDate;public CreditCardStrategy(String name, String cardNumber, String cvv, String expiryDate) {this.name = name;this.cardNumber = cardNumber;this.cvv = cvv;this.expiryDate = expiryDate;}@Overridepublic void pay(double amount) {System.out.printf("Paid %.2f using credit card: %s\n", amount, cardNumber);// 实际支付逻辑...}@Overridepublic boolean validatePaymentDetails() {// 验证信用卡信息return cardNumber != null && !cardNumber.isEmpty() && cvv != null && cvv.length() == 3&& expiryDate != null && expiryDate.matches("\\d{2}/\\d{2}");}
}// 具体策略类:PayPal支付
public class PayPalStrategy implements PaymentStrategy {private String email;private String password;public PayPalStrategy(String email, String password) {this.email = email;this.password = password;}@Overridepublic void pay(double amount) {System.out.printf("Paid %.2f using PayPal: %s\n", amount, email);// 实际支付逻辑...}@Overridepublic boolean validatePaymentDetails() {// 验证PayPal账户return email != null && email.contains("@")&& password != null && password.length() >= 6;}
}// 具体策略类:加密货币支付
public class CryptoCurrencyStrategy implements PaymentStrategy {private String walletAddress;public CryptoCurrencyStrategy(String walletAddress) {this.walletAddress = walletAddress;}@Overridepublic void pay(double amount) {System.out.printf("Paid %.2f using cryptocurrency to wallet: %s\n", amount, walletAddress);// 实际支付逻辑...}@Overridepublic boolean validatePaymentDetails() {// 验证钱包地址return walletAddress != null && walletAddress.startsWith("0x") && walletAddress.length() == 42;}
}// 上下文类:购物车
public class ShoppingCart {private PaymentStrategy paymentStrategy;private List<Item> items = new ArrayList<>();public void setPaymentStrategy(PaymentStrategy strategy) {this.paymentStrategy = strategy;}public void addItem(Item item) {items.add(item);}public void checkout() {if (paymentStrategy == null) {throw new IllegalStateException("Payment strategy not set");}if (!paymentStrategy.validatePaymentDetails()) {throw new IllegalArgumentException("Invalid payment details");}double total = calculateTotal();paymentStrategy.pay(total);items.clear();}private double calculateTotal() {return items.stream().mapToDouble(Item::getPrice).sum();}
}// 客户端代码
public class Main {public static void main(String[] args) {ShoppingCart cart = new ShoppingCart();// 添加商品cart.addItem(new Item("Design Patterns Book", 49.99));cart.addItem(new Item("Wireless Mouse", 25.50));// 选择支付方式并结账PaymentStrategy creditCard = new CreditCardStrategy("John Doe", "1234567890123456", "123", "12/25");cart.setPaymentStrategy(creditCard);cart.checkout();// 更换支付方式PaymentStrategy paypal = new PayPalStrategy("john.doe@example.com", "password123");cart.setPaymentStrategy(paypal);cart.addItem(new Item("USB Cable", 9.99));cart.checkout();}
}

示例解析

在这个扩展后的示例中,我们:

  1. 增强了策略接口,增加了支付验证方法

  2. 添加了新的加密货币支付策略

  3. 完善了购物车上下文,增加了商品管理和总价计算功能

  4. 在结账时增加了策略验证

这个实现展示了策略模式在实际业务中的典型应用,体现了以下优势:

  • 易于扩展:新增支付方式只需添加新的策略类,无需修改现有代码

  • 职责清晰:每种支付方式的逻辑封装在各自的策略类中

  • 灵活切换:运行时可以动态改变支付策略

  • 便于测试:每种策略可以独立测试

策略模式的最佳实践

1. 何时使用策略模式

策略模式特别适用于以下场景:

  1. 一个系统需要在多种算法中选择一种:如排序算法、压缩算法、加密算法等

  2. 一个类有多种行为,且这些行为以条件语句形式出现:可以将每个分支移到各自的策略类中

  3. 需要隐藏算法实现细节:客户端不需要知道算法的具体实现

  4. 算法需要自由切换:如根据性能需求切换不同的算法实现

2. 策略选择机制

策略的选择可以通过以下几种方式实现:

  1. 客户端显式选择:由客户端代码直接创建并设置具体策略

  2. 工厂方法:根据参数创建相应的策略对象

  3. 配置文件:从配置文件中读取策略类型并动态创建

  4. 自动选择:根据系统状态或输入参数自动选择最佳策略

3. 策略对象的创建与管理

对于频繁创建的策略对象,可以考虑:

  1. 享元模式:如果策略是无状态的或可以共享,可以使用享元模式减少对象创建

  2. 对象池:对于创建成本高的策略对象,可以使用对象池管理

  3. 依赖注入:在Spring等框架中,可以通过依赖注入管理策略对象

4. 与其它模式的结合

策略模式常与以下模式结合使用:

  1. 工厂模式:用于创建策略对象

  2. 模板方法模式:在策略接口中定义算法骨架,具体策略实现特定步骤

  3. 装饰器模式:动态增强策略对象的功能

  4. 组合模式:将多个策略组合成更复杂的策略

策略模式的优缺点分析

优点

  1. 开闭原则:无需修改上下文即可引入新策略

  2. 消除条件语句:避免了大量的条件判断

  3. 算法复用:相同算法可以在不同上下文中使用

  4. 灵活性:运行时可以切换算法

  5. 可测试性:每个策略可以独立测试

缺点

  1. 客户端必须了解不同策略:客户端需要知道有哪些策略及各自的适用场景

  2. 策略类数量增加:每个算法一个类,可能导致类数量膨胀

  3. 通信开销:策略与上下文之间可能需要交换数据,增加了复杂性

  4. 对象创建开销:频繁创建销毁策略对象可能影响性能

实际应用案例

1. Java集合框架中的排序

Java的Collections.sort()方法允许传入自定义的Comparator,这实际上是策略模式的应用:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");// 使用不同的排序策略
Collections.sort(names); // 自然排序
Collections.sort(names, String.CASE_INSENSITIVE_ORDER); // 忽略大小写
Collections.sort(names, Comparator.reverseOrder()); // 逆序

2. Spring框架中的资源访问

Spring的ResourceLoader使用策略模式来支持不同的资源定位方式:

Resource template = ctx.getResource("classpath:some/resource/path");
Resource template = ctx.getResource("file:///some/resource/path");
Resource template = ctx.getResource("http://example.com/resource");

3. Java加密体系

JCE(Java Cryptography Extension)使用策略模式支持不同的加密算法:

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);

总结

策略模式是一种强大而灵活的设计模式,它通过将算法封装为独立的策略类,实现了算法定义和使用的分离。这种分离使得算法可以独立于客户端变化,符合开闭原则,提高了代码的可维护性和可扩展性。

在实际开发中,策略模式特别适用于以下情况:

  • 系统需要使用多种算法变体

  • 存在许多条件语句来选择不同的算法

  • 算法需要灵活切换或扩展

通过合理使用策略模式,我们可以创建出更加灵活、可维护的软件系统。然而,也需要注意策略模式可能带来的类数量增加和客户端复杂性提高的问题。在简单场景下,过度使用设计模式可能会适得其反,因此需要根据实际情况权衡利弊。

 

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

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

相关文章

概率/期望 DP llya and Escalator

题目链接&#xff1a;Problem - D - Codeforces 看了这篇文章来的&#xff1a;【算法学习笔记】概率与期望DP - RioTian - 博客园 这篇博客写得挺好的&#xff0c;讲了一些常见方法&#xff0c;概率 / 期望的题多练练就上手了。 题目大意&#xff1a; n 个人排队上电梯&…

大陆电子MBDS开发平台转到其他国产控制器平台产生的问题记录

u8_StComLowSpdGearSwt变量为例&#xff0c;之前用的时候只有输入&#xff0c;没什么实际意义&#xff0c;导致新环境下编译报错&#xff0c;缺少声明&#xff0c;解决办法&#xff1a;注释掉输入模块。今天解决的另一个比较大的问题&#xff0c;不同模型函数公用函数模块生成代…

机器学习模型调优实战指南

文章目录模型选择与调优&#xff1a;从理论到实战1. 引言2. 模型评估&#xff1a;为选择提供依据2.1 偏差-方差权衡2.2 数据集划分与分层抽样2.3 交叉验证&#xff08;Cross-Validation&#xff09;2.4 信息准则&#xff08;AIC / BIC&#xff09;3. 超参数调优&#xff1a;让模…

【教程】Unity CI/CD流程

测试机&#xff1a;红帽 Linux8 源码仓库&#xff1a;Gitee - MrRiver/Unity Example   系统环境准备 1&#xff09;yum 源 sudo curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-8.repo sudo sed -i s/\$releasever/8/g /etc/yum.repos…

文献阅读 | Briefings in Bioinformatics | Hiplot:全面且易于使用的生物医学可视化分析平台

文献介绍文献题目&#xff1a; Hiplot&#xff1a;一个综合且易于使用的 Web 服务&#xff0c;用于增强出版物准备的生物医学数据可视化 研究团队&#xff1a; Openbiox/Hiplot 社区 发表时间&#xff1a; 2022-07-05 发表期刊&#xff1a; Briefings in Bioinformatics 影响因…

【数字图像处理系列笔记】Ch04:灰度变换与空间域图像增强(2)

目录 一、空域滤波基础 一、空域滤波的基本概念 二、空域滤波的数学原理 三、空域滤波器的分类与典型示例 &#xff08;一&#xff09;线性滤波器&#xff08;Linear Filter&#xff09; &#xff08;二&#xff09;非线性滤波器&#xff08;Non-linear Filter&#xff0…

AI浪潮下,FPGA如何实现自我重塑与行业变革

引言&#xff1a;AI 与 FPGA&#xff0c;新时代的碰撞 2025 年&#xff0c;人工智能技术迎来爆发式增长&#xff0c;大模型、生成式 AI 和多模态技术持续突破&#xff0c;人形机器人量产元年正式开启&#xff0c;自动驾驶商业化进程加速&#xff0c;工业数字化转型全面铺开(1)…

系统集成项目管理工程师【第十一章 规划过程组】定义范围、创建WBS、规划进度管理和定义活动篇

系统集成项目管理工程师【第十一章 规划过程组】定义范围、创建WBS、规划进度管理和定义活动篇 一、定义范围&#xff1a;给项目画好"边界线" 定义范围是明确项目和产品"做什么、不做什么"的过程&#xff0c;直接影响后续所有工作的方向。 1. 核心概念与作…

Spring Boot 参数校验全指南

Spring Boot 参数校验全指南 在 Web 开发中&#xff0c;参数校验是保障接口安全性和数据合法性的关键环节。手动编写校验逻辑不仅繁琐&#xff0c;还容易遗漏边界情况。Spring Boot 整合了 validation 工具&#xff0c;提供了一套简洁高效的参数校验方案&#xff0c;可快速实现…

常用技术资料链接

1.team技术 https://zhuanlan.zhihu.com/p/11389323664 https://blog.csdn.net/Lucky_Lu0/article/details/121697151 2.bond切换主备 https://www.xgss.net/3306.html 3.ssh详解&#xff1a; https://cloud.tencent.com/developer/news/105165 https://blog.huochengrm.c…

【Spring Cloud】-- 注册中心

文章目录1. 什么是注册中心2. CPA理论1. 什么是注册中心 注册中心有三种角色&#xff1a; 服务提供者&#xff08;Server&#xff09; &#xff1a;提供接口给其他微服务的程序。服务消费者&#xff08;Client&#xff09;&#xff1a;调用其他微服务提供的接口。**服务注册中…

go-zero 详解

go-zero 详解 go-zero 是一个基于 Go 语言的微服务框架&#xff0c;由字节跳动团队开发并开源&#xff0c;旨在帮助开发者快速构建高可用、高性能的微服务架构。它集成了丰富的组件&#xff0c;简化了微服务开发中的常见问题&#xff08;如服务注册发现、配置管理、限流熔断等&…

接口自动化框架封装之统一请求封装及通过文件实现接口关联

接口自动化测试框架封装目的:简化自动化框架的落地,提高投入和产出比,只要一个人封装好框架,另外的测试通过写yaml测试用例即可实现接口自动化1.统一请求的封装去除多余重复的代码可跨py文件实现通过一个session来自动关联有cookie的接口设置统一公共参数,统一文件处理,统一异常…

Vue 最佳实践:如何利用唯一 key 值保证 el-table 动态渲染的稳定性

&#x1f4cb; 问题描述 在Vue 2.0 ElementUI项目的偏置条件管理页面中&#xff0c;每次切换到"内规拉偏"菜单时&#xff0c;表格样式会发生崩溃&#xff0c;导致表格布局异常、列宽错乱、固定列显示不正确等问题。 &#x1f50d; 问题分析 通过深入分析代码&#x…

popen开启进程,写入数据

通过管道&#xff08;popen&#xff09;启动 SDIWAN_WEB 进程并写入 JSON 数据的过程可以分为以下步骤&#xff0c;结合代码示例和关键注意事项进行说明&#xff1a;1. 核心代码示例 #include <stdio.h> #include <json-c/json.h>int main() {// 1. 创建 JSON 对象…

计算机视觉的四项基本任务辨析

计算机视觉是使计算机能理解采集设备采集的图像视频的一门学科&#xff0c;目的是让计算机实现人的视觉功能——对客观世界的三维场景的感知、识别和理解。换句话说&#xff0c;要让计算机具备通过二维图像认识三维环境的能力。 目录 三个阶段 视觉层级 基本任务 技术难点…

iostat 系统IO监控命令学习

一、iostat 命令描述 “iostat”命令用于监测系统输入/输出设备的负载情况&#xff0c;其通过观察设备处于活跃状态的时间与平均传输速率之间的关系来实现这一目的。该命令会生成报告&#xff0c;这些报告可用于调整系统配置&#xff0c;以更好地平衡物理磁盘之间的输入/输出负…

jenkins使用ssh方式连接gitee 公钥、私钥配置、指纹

前言 Gitee 提供了基于 SSH 协议的 Git 服务&#xff0c;jenkins可使用ssh方式连接gitee&#xff0c;拉取代码、提交tag等&#xff1b;使用ssh 连接&#xff0c;相比用户名密码方式&#xff0c;可省去因密码变更而引起的jenkins关联修改。 gitee生成、添加 SSH 公钥 生成SSH…

如何在Android设备上删除多个联系人(3种方法)

如果您想清理安卓手机&#xff0c;或者只是想删除旧的、不需要的联系人&#xff0c;或者删除多个联系人&#xff0c;有三种有效的方法可供选择。无论您是想手动删除安卓手机上的联系人&#xff0c;还是使用专用工具&#xff0c;都可以按照以下步骤操作。方法1&#xff1a;如何通…

Angular进阶之十三:Angular全新控制流:革命性的模板语法升级

随着Angular v17的发布&#xff0c;框架带来了革命性的控制流语法&#xff0c;彻底改变了我们编写模板的方式。这些改进不仅仅是语法糖——它们提升了性能、开发体验和代码可维护性。 为什么我们需要新的控制流&#xff1f; 在之前的Angular版本中&#xff0c;我们使用结构指令…