当前位置: 首页 > news >正文

策略模式的实现与应用:掌握灵活算法切换的技巧

文章目录

  • 常用的设计模式有以下几种:
    • 一.创建型模式(Creational Patterns):
    • 二.结构型模式(Structural Patterns):
    • 三.行为型模式(Behavioral Patterns):
    • 四.并发模式(Concurrent Patterns):
  • 策略模式概念
  • 一、策略模式例子一
  • 二、策略模式例子二
  • 三、策略模式例子三
  • 总结


常用的设计模式有以下几种:

一.创建型模式(Creational Patterns):

工厂模式(Factory Pattern)
抽象工厂模式(Abstract Factory Pattern)
单例模式(Singleton Pattern)
原型模式(Prototype Pattern)
建造者模式(Builder Pattern)

二.结构型模式(Structural Patterns):

适配器模式(Adapter Pattern)
桥接模式(Bridge Pattern)
装饰器模式(Decorator Pattern)
组合模式(Composite Pattern)
外观模式(Facade Pattern)
享元模式(Flyweight Pattern)
代理模式(Proxy Pattern)

三.行为型模式(Behavioral Patterns):

策略模式(Strategy Pattern)
观察者模式(Observer Pattern)
迭代器模式(Iterator Pattern)
命令模式(Command Pattern)
模板方法模式(Template Method Pattern)
职责链模式(Chain of Responsibility Pattern)
状态模式(State Pattern)
访问者模式(Visitor Pattern)
中介者模式(Mediator Pattern)
备忘录模式(Memento Pattern)
解释器模式(Interpreter Pattern)

四.并发模式(Concurrent Patterns):

单例模式(Singleton Pattern,在多线程环境下的应用)
生成器模式(Builder Pattern,在多线程环境下的应用)
保护性暂停模式(Guarded Suspension Pattern)
观察者模式(Observer Pattern,在多线程环境下的应用)

这些设计模式涵盖了对象的创建、对象间的组织和交互、以及对象的行为等方面,可以帮助开发者更好地组织和设计代码,提高代码的可扩展性、可维护性以及重用性。需要根据实际情况选择适合的设计模式来解决问题。


策略模式概念

今天来讲一讲策略模式:
策略模式(Strategy Pattern)是一种行为设计模式,它允许你定义一系列算法,并将每个算法封装到独立的类中,使它们可以互相替换。策略模式使得算法的变化独立于使用它们的客户端。
在策略模式中,有一个上下文(Context)对象,它持有一个策略接口的引用,并通过调用策略接口的方法来执行具体的算法。算法类实现了策略接口,每个算法类都封装了一种具体的算法,客户端可以根据需要选择不同的策略对象,从而实现不同的行为。🤩🤩🤩
策略模式的优点包括:

1. 算法可以灵活地替换或新增,不影响使用算法的客户端。
2. 可以消除大量的条件语句,提高代码的可维护性和可读性。
3. 可以将算法的实现细节封装起来,提高代码的模块性和复用性。

策略模式在许多场景下都有应用,比如排序算法、计算费用、验证用户等。通过使用策略模式,可以优雅地处理复杂的逻辑,并提供一种可扩展、易于维护的解决方案。🤗🤗🤗

我并不喜欢看含义,对于这种概念性的东西,动手实践才是硬道理!
以下举出 策略模式 的几个 荔枝!!!😍😍😍
相信看完会更加理解策略模式的强大性!

在这里插入图片描述

一、策略模式例子一

假设我们正在开发一个商场收银系统,其中有不同的折扣策略供用户选择,包括无折扣、固定折扣和打折折扣。我们希望通过策略模式来实现这些不同的折扣策略。
首先,我们定义一个抽象的策略接口 DiscountStrategy,它声明了一个计算折扣金额的方法 calculateDiscount:

public interface DiscountStrategy {double calculateDiscount(double amount);
}

然后,我们分别实现三种具体的策略类:NoDiscountStrategy(无折扣)、FixedDiscountStrategy(固定折扣)和PercentageDiscountStrategy(打折折扣)。它们都实现了 DiscountStrategy 接口:

public class NoDiscountStrategy implements DiscountStrategy {@Overridepublic double calculateDiscount(double amount) {return 0; // 无折扣,返回0}
}public class FixedDiscountStrategy implements DiscountStrategy {private double discount;public FixedDiscountStrategy(double discount) {this.discount = discount;}@Overridepublic double calculateDiscount(double amount) {return discount; // 固定折扣,直接返回固定的折扣金额}
}public class PercentageDiscountStrategy implements DiscountStrategy {private double percentage;public PercentageDiscountStrategy(double percentage) {this.percentage = percentage;}@Overridepublic double calculateDiscount(double amount) {return amount * percentage; // 打折折扣,根据折扣比例计算折扣金额}
}

最后,我们定义一个上下文类 Cashier,它持有一个 DiscountStrategy 的引用,并在结算时调用该策略的方法:

public class Cashier {private DiscountStrategy discountStrategy;public void setDiscountStrategy(DiscountStrategy discountStrategy) {this.discountStrategy = discountStrategy;}public double calculateTotalWithDiscount(double amount) {double discount = discountStrategy.calculateDiscount(amount);return amount - discount;}
}

现在,我们可以使用策略模式来实现商场收银系统的折扣功能了。示例如下:

public class Main {public static void main(String[] args) {Cashier cashier = new Cashier();// 设置无折扣策略DiscountStrategy noDiscountStrategy = new NoDiscountStrategy();cashier.setDiscountStrategy(noDiscountStrategy);double total = cashier.calculateTotalWithDiscount(100);System.out.println("Total with no discount: " + total);// 设置固定折扣策略DiscountStrategy fixedDiscountStrategy = new FixedDiscountStrategy(20);cashier.setDiscountStrategy(fixedDiscountStrategy);total = cashier.calculateTotalWithDiscount(100);System.out.println("Total with fixed discount: " + total);// 设置打折折扣策略DiscountStrategy percentageDiscountStrategy = new PercentageDiscountStrategy(0.1);cashier.setDiscountStrategy(percentageDiscountStrategy);total = cashier.calculateTotalWithDiscount(100);System.out.println("Total with percentage discount: " + total);}
}

输出结果:

Total with no discount: 100.0
Total with fixed discount: 80.0
Total with percentage discount: 90.0

通过策略模式,我们可以灵活地选择不同的折扣策略,并在运行时动态地切换和应用这些策略,而不需要修改 Cashier 类的代码。这样,我们可以方便地添加新的折扣策略或修改现有策略的实现,而不会对其他部分产生影响。这样的设计符合开闭原则,增加了代码的可维护性和扩展性。

二、策略模式例子二

例子2:排序算法
假设我们正在设计一个排序工具,它可以根据用户选择的不同排序算法对数据进行排序。我们可以使用策略模式来实现不同的排序策略。
首先,我们定义一个排序策略接口 SortingStrategy,它声明了一个排序方法 sort:

public interface SortingStrategy {void sort(int[] array);
}

然后,我们分别实现两种具体的策略类:BubbleSortStrategy(冒泡排序)和 QuickSortStrategy(快速排序)。它们都实现了 SortingStrategy 接口:

public class BubbleSortStrategy implements SortingStrategy {@Overridepublic void sort(int[] array) {// 冒泡排序算法的具体实现// ...}
}public class QuickSortStrategy implements SortingStrategy {@Overridepublic void sort(int[] array) {// 快速排序算法的具体实现// ...}
}

最后,我们定义一个上下文类 Sorter,它持有一个 SortingStrategy 的引用,并在排序时调用该策略的方法:

public class Sorter {private SortingStrategy sortingStrategy;public void setSortingStrategy(SortingStrategy sortingStrategy) {this.sortingStrategy = sortingStrategy;}public void sortArray(int[] array) {sortingStrategy.sort(array);}
}

现在,我们可以使用策略模式来实现排序工具了。示例如下:

public class Main {public static void main(String[] args) {Sorter sorter = new Sorter();// 使用冒泡排序策略SortingStrategy bubbleSortStrategy = new BubbleSortStrategy();sorter.setSortingStrategy(bubbleSortStrategy);int[] array = {5, 3, 8, 2, 1};sorter.sortArray(array);System.out.println("Sorted array using bubble sort:");for (int num : array) {System.out.print(num + " ");}System.out.println();// 使用快速排序策略SortingStrategy quickSortStrategy = new QuickSortStrategy();sorter.setSortingStrategy(quickSortStrategy);int[] array2 = {9, 4, 6, 2, 7};sorter.sortArray(array2);System.out.println("Sorted array using quick sort:");for (int num : array2) {System.out.print(num + " ");}System.out.println();}
}

输出结果:

Sorted array using bubble sort:
1 2 3 5 8 
Sorted array using quick sort:
2 4 6 7 9

通过策略模式,我们可以灵活地选择不同的排序策略,而不需要修改 Sorter 类的代码。这样,我们可以方便地添加新的排序策略或修改现有策略的实现,而不会对其他部分产生影响。

三、策略模式例子三

假设我们正在开发一个电商平台,针对不同的用户类型(普通用户、VIP用户、管理员),我们需要为其提供不同的登录方式。
首先,我们定义一个登录策略接口 LoginStrategy,它声明了一个登录方法 login:

public interface LoginStrategy {void login();
}

然后,我们分别实现三种具体的策略类:DefaultLoginStrategy(普通用户登录方式)、VIPLoginStrategy(VIP用户登录方式)和 AdminLoginStrategy(管理员登录方式)。它们都实现了 LoginStrategy 接口:

public class DefaultLoginStrategy implements LoginStrategy {@Overridepublic void login() {System.out.println("使用用户名和密码登录");// 具体的登录逻辑}
}public class VIPLoginStrategy implements LoginStrategy {@Overridepublic void login() {System.out.println("使用手机号和密码登录");// 具体的登录逻辑}
}public class AdminLoginStrategy implements LoginStrategy {@Overridepublic void login() {System.out.println("使用管理员账号和密码登录");// 具体的登录逻辑}
}

最后,我们定义一个上下文类 LoginContext,它持有一个 LoginStrategy 的引用,并在登录时调用该策略的方法:

public class LoginContext {private LoginStrategy loginStrategy;public void setLoginStrategy(LoginStrategy loginStrategy) {this.loginStrategy = loginStrategy;}public void performLogin() {loginStrategy.login();}
}

现在,我们可以使用策略模式来为不同的用户类型提供不同的登录方式了。示例如下:

public class Main {public static void main(String[] args) {LoginContext loginContext = new LoginContext();// 普通用户登录LoginStrategy defaultLoginStrategy = new DefaultLoginStrategy();loginContext.setLoginStrategy(defaultLoginStrategy);loginContext.performLogin();// VIP用户登录LoginStrategy vipLoginStrategy = new VIPLoginStrategy();loginContext.setLoginStrategy(vipLoginStrategy);loginContext.performLogin();

总结

UU们,懂了嘛!!!😄😄😄
以下是实际开发中常用的几个例子,展示了策略模式的应用:

支付方式:
在电商平台上,用户可以选择不同的支付方式进行支付,比如支付宝、微信支付、银行卡支付等。这些支付方式都具备支付的功能,但其实现方式和支付逻辑可能有所不同。策略模式可以用于将不同的支付方式封装成不同的策略类,然后在运行时根据用户选择的支付方式来选择相应的策略进行支付。

缓存策略:
在系统开发中,我们经常会使用缓存来提高系统性能。不同的数据操作可能需要不同的缓存策略,比如基于时间的过期策略、基于LRU(Least
Recently Used)的淘汰策略、基于LFU(Least Frequently
Used)的淘汰策略等。策略模式可以用于封装不同的缓存策略为不同的策略类,然后在运行时选择相应的缓存策略进行数据操作。

图像处理:
在图像处理应用中,我们可能需要应用不同的滤镜或特效来处理图像,比如黑白滤镜、模糊滤镜、锐化滤镜等。这些滤镜都具备处理图像的功能,但实现方式和处理逻辑可能有所不同。策略模式可以用于将不同的滤镜或特效封装成不同的策略类,然后在运行时根据用户选择的滤镜或特效来选择相应的策略进行图像处理。

日志记录级别:
在日志记录应用中,不同的日志级别对应不同的日志输出方式,比如普通日志输出到控制台,警告日志输出到文件,错误日志发送邮件等等。策略模式可以用于封装不同的日志记录级别为不同的策略类,然后在运行时根据日志级别选择相应的策略进行日志记录。

这些都是在实际开发中常用的例子,策略模式可以帮助我们根据不同的条件和需求来选择相应的策略进行处理,提供了灵活性和扩展性。它可以将不同的策略封装起来,使客户端代码与具体策略的实现解耦,从而提高代码的可维护性和可测试性。
(注意:是不同的处理逻辑,如果你只是根据不同类型查数据库一张表的数据,那就没啥必要了)
不懂或者有什么错误的地方,欢迎评论区留言,俺可以再举更多的 荔枝~~~

@作者:加辣椒了吗?
简介:憨批大学生一枚,喜欢在博客上记录自己的学习心得,也希望能够帮助到你们!
在这里插入图片描述

相关文章:

策略模式的实现与应用:掌握灵活算法切换的技巧

文章目录 常用的设计模式有以下几种:一.创建型模式(Creational Patterns):二.结构型模式(Structural Patterns):三.行为型模式(Behavioral Patterns):四.并发…...

当ChatGPT应用在汽车行业,具体有哪些场景?

​ ChatGPT有潜力彻底改变汽车行业并将其提升到新的高度。在ChatGPT的加持下,该行业的多个领域都将取得重大变化。 利用ChatGPT作更高级的虚拟助理 你可能用过现有的虚拟助理,它们一系列的回复有时候让人不得不感叹一句“人工智障”!然而&a…...

行为型-中介者模式(Mediator Pattern)

概述 中介者模式(Mediator Pattern)是一种行为型设计模式,它通过封装一系列对象之间的交互方式,使这些对象能够互相通信而不需要直接相互引用。中介者模式通过集中控制对象的交互,使得对象之间的耦合度降低&#xff0…...

Kibana+Prometheus+node_exporter 监控告警部署

下载好三个软件包 一、prometheus安装部署 1、解压 linxxubuntu:~/module$ tar -xvf prometheus-2.45.0-rc.0.linux-amd64.tar.gz 2、修改配置文件的IP地址 # my global config global:scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is ever…...

【前端知识】JavaScript——设计模式(工厂模式、构造函数模式、原型模式)

【前端知识】JavaScript——设计模式(工厂模式、构造函数模式、原型模式) 一、工厂模式 工厂模式是一种众所周知的设计模式,广泛应用于软件工程领域,用于抽象创建特定对象的过程。 优点:可以解决创建多个类似对象的…...

未来的算法备案法规:创新和安全如何兼顾?

随着科技的快速发展,算法正逐步渗透到我们生活的各个方面,从推荐引擎到自动驾驶,从医疗诊断到金融交易,这一现象既充满希望,也充满了挑战。其中一个关键的挑战就是如何设计和实施有效的算法备案法规,以促进…...

pycharm 使用远程服务器 jupyter (本地jupyter同理)

1. 远程服务器miniconda 环境中创建jupyter环境 # 1. 激活环境 conda activate envname#2. 在环境中安装jupyter pip install jupyter # 或者 conda install jupyter#3. 生成jupyter_notebook_config.py文件 jupyter notebook --generate-config#4. 设置密码 jupyter noteboo…...

leetcode 376. 摆动序列

2023.7.28 本题思路是定义一个 direct变量记录上一次摆动是上坡还是下坡 。 然后在一个for循环中循环判断当前摆动和上一次摆动是否一致,如果不一致则视为一次摆动。 如果前后元素值相等得话,直接continue进入下一次循环。 下面看代码: clas…...

【图像处理】使用自动编码器进行图像降噪(改进版)

阿里雷扎凯沙瓦尔兹 一、说明 自动编码器是一种学习压缩和重建输入数据的神经网络。它由一个将数据压缩为低维表示的编码器和一个从压缩表示中重建原始数据的解码器组成。该模型使用无监督学习进行训练,旨在最小化输入和重建输出之间的差异。自动编码器可用于降维、…...

MySQL大数据量分页查询方法及其优化

---方法1: 直接使用数据库提供的SQL语句 ---语句样式: MySQL中,可用如下方法: SELECT * FROM 表名称 LIMIT M,N ---适应场景: 适用于数据量较少的情况(元组百/千级) ---原因/缺点: 全表扫描,速度会很慢 且 有的数据库结果集返回不稳定(如某次返回1,2,3,另外的一次返回2,1,3). L…...

dataTable转成对象、json、list

datatable转换成list集合 public static T TableToEntity<T>(DataTable dt, int rowindex 0, bool isStoreDB true){Type type typeof(T);T entity Activator.CreateInstance<T>();if (dt null){return entity;}DataRow row dt.Rows[rowindex];PropertyInfo…...

ubuntu环境安装centos7虚拟机网络主机不可达,ping不通

【NAT模式下解决】1.首先vi /etc/sysconfig/network-scripts/ifcfg-ens33检查ONBOOTyes&#xff0c;保存 2.输入systemctl restart network命令重启网关...

STN:Spatial Transformer Networks

1.Abstract 卷积神经网络缺乏对输入数据保持空间不变的能力&#xff0c;导致模型性能下降。作者提出了一种新的可学习模块&#xff0c;STN。这个可微模块可以插入现有的卷积结构中&#xff0c;使神经网络能够根据特征图像本身&#xff0c;主动地对特征图像进行空间变换&#x…...

C语言学习笔记 VScode设置C环境-06

目录 一、下载vscode软件 二、安装minGW软件 三、VS Code安装C/C插件 3.1 搜索并安装C/C插件 3.2 配置C/C环境 总结 一、下载vscode软件 在官网上下载最新的版本 Download Visual Studio Code - Mac, Linux, Windowshttps://code.visualstudio.com/download 二、安装minGW…...

alias取别名后,另一个shell中和shell脚本中不生效的问题以及crontab执行docker失败问题

目录 问题一&#xff1a;用alias取别名后&#xff0c;另一个shell中不生效描述原因解决 问题二&#xff1a;用alias取别名后&#xff0c;别名在脚本中不生效描述原因解决 问题三&#xff1a;crontab计划任务不能运行docker命令描述原因解决 问题一&#xff1a;用alias取别名后&…...

Shell ❀ 一键配置Iptables规则脚本 (HW推荐)

文章目录 注意事项1. 地址列表填写规范2. 代码块3. 执行结果4. 地址与端口获取方法4.1 tcpdump抓包分析&#xff08;推荐使用&#xff09;4.2 TCP连接分析&#xff08;仅能识别TCP连接&#xff09; 注意事项 请务必按照格式填写具体参数&#xff0c;否则会影响到匹配规则的创建…...

linux服务器查找大文件及删除文件后磁盘空间没有得到释放

1、查询服务器中大于1G的文件 find / -type f -size 1G这条命令是查询自”/”根目录下所有大小超过1G的文件&#xff0c;查询的大小可以根据需要改变&#xff0c;如下&#xff1a; 相关查询&#xff1a;查询服务器中大于100M的文件 find / -type f -size 100M2、查询服务器中…...

Java那些“锁”事 - 死锁及排查

死锁是两个或者两个以上的线程在执行过程中&#xff0c;因争夺资源而造成的一种互斥等待现象&#xff0c;若没有外界干涉那么它们将无法推进下去。如果系统资源不足&#xff0c;进程的资源请求都得到满足&#xff0c;死锁出现的可能性就很低&#xff0c;否则就会因为争夺有限的…...

LLM系列 | 18 : 如何用LangChain进行网页问答

简介 一夕轻雷落万丝&#xff0c;霁光浮瓦碧参差。 紧接之前LangChain专题文章&#xff1a; 15:如何用LangChain做长文档问答&#xff1f;16:如何基于LangChain打造联网版ChatGPT&#xff1f;17:ChatGPT应用框架LangChain速成大法 今天这篇小作文是LangChain实践专题的第4…...

Aspose.cell excel转pdf日期格式不正确yyyy/MM/dd变成MM/dd/yyyy

最近使用Aspose.cell将excel转pdf过程中excel中时间格式列的显示和excel表里的值显示不一样。 excel里日期格式 yyyy/MM/dd pdf里日期格式MM/dd/yyyy 主要原因&#xff1a;linux和windows里内置的时间格式不一致&#xff0c;当代码部署到linux服务器的时候转换格式就会发生不一…...

vscode(仍待补充)

写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh&#xff1f; debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

C#学习第29天:表达式树(Expression Trees)

目录 什么是表达式树&#xff1f; 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持&#xff1a; 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)

引言 在人工智能飞速发展的今天&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;已成为技术领域的焦点。从智能写作到代码生成&#xff0c;LLM 的应用场景不断扩展&#xff0c;深刻改变了我们的工作和生活方式。然而&#xff0c;理解这些模型的内部…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…...

tauri项目,如何在rust端读取电脑环境变量

如果想在前端通过调用来获取环境变量的值&#xff0c;可以通过标准的依赖&#xff1a; std::env::var(name).ok() 想在前端通过调用来获取&#xff0c;可以写一个command函数&#xff1a; #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...