Java策略模式源码剖析及使用场景

news/2024/7/27 8:04:39/文章来源:https://blog.csdn.net/weixin_44716935/article/details/136630610

策略模式

  • 一、介绍
  • 二、不同的支付方式采用不同的策略
  • 三、 电商定价策略
  • 四、日志记录策略
  • 五、 压缩算法
  • 六、Java 中的 `Arrays.sort()` 方法,不同的排序策略进行排序
  • 七、Spring 中的 `ResourceLoader` 类,不同的资源位置采用不同的加载策略

一、介绍

策略模式是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户端。

策略模式的主要角色有

  1. Context(上下文): 负责维护一个具体的策略类实例,并根据它来协调算法的执行。

  2. Strategy(抽象策略): 定义了一个算法族,声明了一个算法的操作,它有一个或多个具体的策略类实现。

  3. ConcreteStrategy(具体策略): 具体实现了 Strategy 接口定义的算法。

策略模式的优点

  1. 算法可以在运行时进行切换,避免了使用复杂的条件语句。
  2. 扩展性良好,增加一个策略只需增加一个具体的策略类即可。
  3. 避免使用多重条件转移语句,利于代码的维护。

策略模式的缺点

  1. 客户端必须知道所有的策略类,并且需要理解具体的策略类,违反了最少知识原则。
  2. 增加了对象的数目。

二、不同的支付方式采用不同的策略

// 支付策略接口
interface PaymentStrategy {boolean pay(double amount);
}// 具体的支付策略实现
class CashPaymentStrategy implements PaymentStrategy {@Overridepublic boolean pay(double amount) {System.out.println("现金支付了 " + amount + " 元");return true;}
}class CreditCardPaymentStrategy implements PaymentStrategy {@Overridepublic boolean pay(double amount) {System.out.println("信用卡支付了 " + amount + " 元");return true;}
}// 上下文类
class PaymentContext {private PaymentStrategy strategy;public void setStrategy(PaymentStrategy strategy) {this.strategy = strategy;}public boolean executePayment(double amount) {return strategy.pay(amount);}
}// 客户端代码
public class Client {public static void main(String[] args) {PaymentContext context = new PaymentContext();context.setStrategy(new CashPaymentStrategy());context.executePayment(100.0); // 现金支付了 100.0 元context.setStrategy(new CreditCardPaymentStrategy());context.executePayment(200.0); // 信用卡支付了 200.0 元}
}
  • PaymentStrategy 接口定义了支付操作的抽象策略。
  • CashPaymentStrategyCreditCardPaymentStrategy 分别实现了现金支付和信用卡支付的具体策略。
  • PaymentContext 类是上下文,它维护了一个策略实例,并提供了执行支付操作的方法。
  • 在客户端代码中,我们可以根据需要动态地设置不同的支付策略,并执行相应的支付操作。
    Java项目中有很多地方都可以使用策略模式,下面我给出一些典型的例子:

三、 电商定价策略

在一个电商项目中,我们可以根据不同的营销策略对商品进行不同的定价。比如,对于普通用户可以按照原价定价,而对于VIP用户则可以打折。我们可以使用策略模式来实现这个需求。

// 定价策略接口
interface PricingStrategy {double calculatePrice(double originalPrice);
}// 原价定价策略
class OriginalPricingStrategy implements PricingStrategy {@Overridepublic double calculatePrice(double originalPrice) {return originalPrice;}
}// VIP折扣策略
class VipDiscountPricingStrategy implements PricingStrategy {private final double discountRate;public VipDiscountPricingStrategy(double discountRate) {this.discountRate = discountRate;}@Overridepublic double calculatePrice(double originalPrice) {return originalPrice * (1 - discountRate);}
}// 定价上下文
class PricingContext {private PricingStrategy strategy;public void setStrategy(PricingStrategy strategy) {this.strategy = strategy;}public double calculatePrice(double originalPrice) {return strategy.calculatePrice(originalPrice);}
}

四、日志记录策略

在一个需要记录日志的系统中,我们可能需要根据不同的日志级别采用不同的日志记录策略。比如,对于DEBUG级别的日志,我们可以将其记录到控制台,而对于ERROR级别的日志,我们则需要将其记录到文件中。这种情况下,就可以使用策略模式。

// 日志记录策略接口
interface LoggingStrategy {void log(String message, LogLevel level);
}// 控制台日志记录策略
class ConsoleLoggingStrategy implements LoggingStrategy {@Overridepublic void log(String message, LogLevel level) {System.out.println("[" + level + "] " + message);}
}// 文件日志记录策略
class FileLoggingStrategy implements LoggingStrategy {@Overridepublic void log(String message, LogLevel level) {// 将日志记录到文件中}
}// 日志记录上下文
class LoggingContext {private LoggingStrategy strategy;public void setStrategy(LoggingStrategy strategy) {this.strategy = strategy;}public void log(String message, LogLevel level) {strategy.log(message, level);}
}

五、 压缩算法

在一个需要进行数据压缩的系统中,我们可能需要根据不同的场景采用不同的压缩算法。比如,对于一般的文本数据,我们可以使用Gzip算法进行压缩,而对于多媒体数据,我们则需要使用更高效的算法。这种情况下,我们可以使用策略模式来实现不同的压缩算法。

// 压缩策略接口
interface CompressionStrategy {byte[] compress(byte[] data);byte[] decompress(byte[] compressedData);
}// Gzip压缩策略
class GzipCompressionStrategy implements CompressionStrategy {@Overridepublic byte[] compress(byte[] data) {// 使用Gzip算法进行压缩}@Overridepublic byte[] decompress(byte[] compressedData) {// 使用Gzip算法进行解压缩}
}// ZLIB压缩策略
class ZlibCompressionStrategy implements CompressionStrategy {@Overridepublic byte[] compress(byte[] data) {// 使用ZLIB算法进行压缩}@Overridepublic byte[] decompress(byte[] compressedData) {// 使用ZLIB算法进行解压缩}
}// 压缩上下文
class CompressionContext {private CompressionStrategy strategy;public void setStrategy(CompressionStrategy strategy) {this.strategy = strategy;}public byte[] compress(byte[] data) {return strategy.compress(data);}public byte[] decompress(byte[] compressedData) {return strategy.decompress(compressedData);}
}

六、Java 中的 Arrays.sort() 方法,不同的排序策略进行排序

Java 的 Arrays.sort() 方法在内部确实使用了策略模式来实现不同的排序算法。下面我们来详细分析一下它的底层实现源码。

Arrays.sort() 方法的实现位于 java.util.Arrays 类中,它根据待排序数组的类型和大小,选择合适的排序算法进行排序。在这个过程中,它使用了策略模式来封装不同的排序算法。

首先,让我们看看 Arrays.sort() 方法的签名:

public static void sort(Object[] a) {// 判断数组类型和长度if (LegacyMergeSort.userRequested)legacyMergeSort(a);elseComputeMaxs.parallelSort(a, null);
}

可以看到,对于对象数组,sort() 方法会根据一个名为 LegacyMergeSort.userRequested 的标志位,选择使用旧的归并排序算法(legacyMergeSort)或者新的并行排序算法(ComputeMaxs.parallelSort)。这里就体现了策略模式的思想,不同的排序算法被封装在不同的策略类中。

接下来,我们看看新的并行排序算法 ComputeMaxs.parallelSort() 的实现:

static <T extends Comparable<? super T>> void parallelSort(T[] a, Comparator<? super T> cmp) {// 根据数组长度选择合适的排序算法int len = a.length;if (len > MIN_ARRAY_SORT_GRAN) {rangeSort(a, 0, len - 1, cmp);} else if (len != 0) {Binarysort.sort(a, 0, len, null, cmp);}
}private static <T extends Comparable<? super T>>
void rangeSort(T[] a, int from, int to, Comparator<? super T> cmp) {// 使用TimSort或归并排序算法进行排序if (from == 0 && to == a.length - 1) {// 使用TimSort算法new TimSort(a, cmp).sort(a, from, to);} else {// 使用归并排序算法new MergeSort(a, cmp, from, to).sort();}
}

在上面的代码中,我们可以看到

  1. parallelSort() 方法根据数组长度选择使用 rangeSort()Binarysort.sort()rangeSort() 用于较大的数组,而 Binarysort.sort() 用于较小的数组。
  2. rangeSort() 方法根据数组的范围,选择使用 TimSort 算法或归并排序(MergeSort)算法。

这里,TimSortMergeSortBinarysort 就是不同的排序策略类。它们都实现了相应的排序算法,而 parallelSort()rangeSort() 充当了策略模式中的上下文(Context)角色,根据具体情况选择合适的策略类。

让我们继续看看 TimSort 的实现

static final class TimSort<T extends Comparable<? super T>> extends MergeSort<T> {// TimSort算法的实现代码...
}

TimSort 类继承自 MergeSort 类,它是一种改进的归并排序算法,对于部分有序的数组有更好的性能表现。

MergeSort 类则实现了传统的归并排序算法

static final class MergeSort<T extends Comparable<? super T>> extends Sorter<T> {// 归并排序算法的实现代码...
}

MergeSort 继承自 Sorter 抽象类,Sorter 定义了一些公共的排序方法和字段,同时也包含了 Binarysort 的实现。

abstract static class Sorter<T extends Comparable<? super T>> {// 一些公共方法和字段...// Binarysort算法的实现static <T extends Comparable<? super T>> void sort(T[] a, int from, int to, ...) {// Binarysort算法实现代码...}
}

通过上面的源码分析,我们可以看到 Java Arrays.sort() 方法是如何使用策略模式来选择合适的排序算法的。不同的排序算法被封装在不同的策略类中,如 TimSortMergeSortBinarysort。而 parallelSort()rangeSort() 方法则根据具体情况选择合适的策略类进行排序。

这种设计使得 Arrays.sort() 方法可以灵活地切换不同的排序算法,也便于后续添加新的排序算法。同时,由于每种算法都被封装在单独的类中,代码的可读性和维护性也得到了提高。

总的来说,Java 的 Arrays.sort() 方法是一个很好的策略模式的应用实例,它展示了如何使用策略模式来封装和选择不同的算法,提高代码的灵活性和可扩展性。

七、Spring 中的 ResourceLoader 类,不同的资源位置采用不同的加载策略

在 Spring 5 中,ResourceLoader 接口使用了策略模式来加载不同类型的资源。它定义了一个统一的接口,而具体的资源加载策略则由不同的实现类来处理。下面我们来分析一下它的底层源码:

ResourceLoader 接口的定义如下:

public interface ResourceLoader {Resource getResource(String location);ClassLoader getClassLoader();
}

它定义了两个方法:

  1. getResource(String location) 用于根据给定的资源位置返回对应的 Resource 对象。
  2. getClassLoader() 返回相关的 ClassLoader

Spring 提供了一个默认的 ResourceLoader 实现类 DefaultResourceLoader。它实现了多种资源加载策略,包括从类路径、文件系统、URL 等不同位置加载资源。

public class DefaultResourceLoader implements ResourceLoader {// ...@Overridepublic Resource getResource(String location) {Assert.notNull(location, "Location must not be null");// 通过 ClassPathContextResource 策略加载类路径资源if (location.startsWith(CLASSPATH_URL_PREFIX)) {return new ClassPathContextResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());}// ... 其他策略}
}

getResource 方法中,根据资源位置的不同前缀,采用不同的资源加载策略。比如,对于以 classpath: 开头的资源位置,它会使用 ClassPathContextResource 策略进行加载。

ClassPathContextResourceClassPathResource 的子类,它实现了从类路径中加载资源的具体策略:

public class ClassPathResource extends AbstractFileResolvingResource {// 获取资源的具体实现...
}

Spring 还提供了其他一些 ResourceLoader 的实现,如:

  • FileSystemResourceLoader: 从文件系统中加载资源
  • ServletContextResourceLoader: 从 Servlet 上下文中加载资源
  • ...

通过组合不同的资源加载策略,Spring 可以非常灵活地从各种不同的位置加载资源。

总的来说,ResourceLoader 接口充当了策略模式中的抽象策略角色,而具体的资源加载策略则由其不同的实现类(如 ClassPathResourceFileSystemResource 等)扮演了具体策略的角色。当需要加载资源时,Spring 会根据资源位置信息选择合适的策略实现,从而完成资源的加载过程。

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

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

相关文章

mysql中的非空间数据导入sqlserver中空间化

以下操作都在Navicat Premium 15软件中操作 1、mysql导出数据 以导出csv为例 不修改导出路径的话默认就是在桌面 设置编码UTF-8 这边还是默认,最好不要修改,如果文本识别符号为空,导入的时候可能字段会错乱 开始即可 2、导入sqlserver数据库中

通过Maven创建Web工程

通过Maven创建Web工程 方式一方式二 方式一 1.先创建一个Maven工程 2.把该Maven模块的pom文件里添加一个war 3.选中该Maven模块 点击项目架构 4.手动添加一个Web架构 方式二 1.也是new一个模块 但是直接配置好Web 2.这里就是我IDEA对Maven的设置 3.第一次创建 可能…

第六篇【传奇开心果系列】Python的自动化办公库技术点案例示例:大学生数据全方位分析挖掘经典案例

传奇开心果博文系列 系列博文目录Python的自动化办公库技术点案例示例系列 博文目录前言一、Pandas库全方位分析挖掘大学生数据能力介绍二、大学生学生成绩数据分析数据挖掘示例代码三、大学生选课数据分析数据挖掘示例代码四、大学生活动参与数据分析数据挖掘示例代码五、大学…

我用Coze给自己的服务号加了一个多功能的GPT服务机器人

我用Coze给自己的服务号加了一个多功能的GPT服务机器人&#xff0c;可以查新闻&#xff0c;交互式回答问题&#xff0c;查快递&#xff0c;画图画&#xff0c;联网回答问题 可以查快递 试用&#xff1a;搜索觉醒AI

Excel判断CD两列在EF两列的列表中是否存在

需求 需要将CD两列的ID和NAME组合起来&#xff0c;查询EF两列的ID和NAME组合起来的列表中是否存在&#xff1f; 比如&#xff0c;判断第二行的“123456ABC”在EF的第二行到第四行中是否存在&#xff0c;若存在则显示Y&#xff0c;不存在则显示N 实现的计算公式 IF(ISNUMBER…

软考72-上午题-【面向对象技术2-UML】-UML中的图3

一、状态图 1-1、状态图的定义 状态图&#xff0c;展现了一个状态机&#xff0c;由&#xff1a;状态、转换、事件和活动组成&#xff0c;是系统的动态视图。 活动(动作) 可以在状态内执行也可以在状态转换(迁移) 时执行。 状态图强调&#xff1a;行为的事件顺序。 1-2、状态图…

4G安卓核心板T310_紫光展锐平台方案

紫光展锐T310应用 DynamlQ架构 12nm 制程工艺&#xff0c;采用 1*Cortex-A753*Cortex-A55处理器&#xff0c;搭载Android11.0操作系统&#xff0c;主频最高达2.0GHz.此外&#xff0c;DynamlQ融入了AI神经网络技术&#xff0c;新增机器学习指令&#xff0c;让其在运算方面的机器…

BigDL-LLM 安装指南——在iGPU集成显卡下使用BigDL-LLM大模型库加速LLM

文章目录 iGPU是什么&#xff1f;一、环境准备1.1 Visual Studio 2022 Community 安装1.2 安装或更新最新版本的GPU驱动程序1.3 安装英特尔oneAPI工具包2024.0版本1.4 安装Anaconda 二、BigDL -LLM 安装2.1 创建虚拟环境2.2 激活虚拟环境2.3 安装bigdl-llm[xpu] 三、运行环境配…

centos命令history设置记录10000行

今天在操作服务器的时候&#xff0c;用history查看操作记录的时候&#xff0c;发现只能查看10条&#xff0c;这样不行啊&#xff0c;我想查看所有人对服务器操作的命令。 [rootbogon ~]# history解决办法&#xff1a; #1、找到/etc/profile文件中的histsize 把10改成10000 […

Netty架构详解

文章目录 概述整体结构Netty的核心组件逻辑架构BootStrap & ServerBootStrapChannelPipelineFuture、回调和 ChannelHandler选择器、事件和 EventLoopChannelHandler的各种ChannelInitializer类图 Protocol Support 协议支持层Transport Service 传输服务层Core 核心层模块…

打卡学习kubernetes——kubernetes架构原理

接上一篇的内容&#xff0c;除了核心组件&#xff0c;还有一些推荐的Add-ons&#xff1a; kube-dns 负责为整个集群提供DNS服务Ingress Controller 为服务提供外网入口Heapster 提供资源监控&#xff08;没用过这个&#xff0c;但是用过grafana&#xff0c;很方便&#xf…

【网络安全】手机不幸被远程监控,该如何破解,如何预防?

手机如果不幸被远程监控了&#xff0c;用三招就可以轻松破解&#xff0c;再用三招可以防范于未然。 三招可破解可解除手机被远程监控 1、恢复出厂设置 这一招是手机解决软件故障和系统故障的终极大招。只要点了恢复出厂设置&#xff0c;你手机里后装的各种APP全部将灰飞烟灭…

Ae 从入门到精通之三:合成与图层

图层 Layer是构建合成的基本单位。 一个图层上可以有一个或多个画面元素&#xff0c;多个图层在时间、空间上有组织地排列&#xff0c;从而创造丰富多彩的画面效果。 Ae 中的图层类似于 Ps 中的图层或 Pr 中的轨道。 合成 Composition是放置图层的“容器”。 每个合成都对应一个…

如何使用“Docker registry创建本地仓库,在服务器之间进行文件push和pull”?

1.1、在服务器1&#xff0c;运行registry docker run -d -p 5000:5000 -v ${PWD}/registry:/var/lib/registry --restart always --name registry registry:2.7.11.2、编辑/etc/docker/daemon.json 文件&#xff0c; 192.168.xxx.xxx 换成你自己 registry 服务的地址 sudo na…

vue 自定义组件绑定model+弹出选择支持上下按键选择

参考地址v-modelhttps://v2.cn.vuejs.org/v2/guide/components-custom-events.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6%E7%9A%84-v-model 原文代码 Vue.component(base-checkbox, {model: {prop: checked,event: change},props: {checked: Boolean},template: `…

通过日志恢复sql server数据库

在SQL Server中&#xff0c;通过日志恢复数据库是一个精细的过程&#xff0c;主要用于在数据库出现错误、数据丢失或需要回滚到特定时间点时恢复数据。以下是一般步骤概述&#xff1a; 设置恢复模式&#xff1a; 首先&#xff0c;数据库必须配置为“完整恢复模式”或“大容量…

Kubernetes专题-01 集群部署篇

kubernetes专题 注&#xff1a; 本教程由羞涩梦整理同步发布&#xff0c;本人技术分享站点&#xff1a;blog.hukanfa.com转发本文请备注原文链接&#xff0c;本文内容整理日期&#xff1a;2024-03-010csdn 博客名称&#xff1a;五维空间-影子&#xff0c;欢迎关注 1 集群规划…

【数据结构取经之路】快速排序及其优化

目录 简介 快速排序的实现步骤 快排的时间复杂度 快排的空间复杂度 霍尔法 证明 key > x left从key的位置出发的原因 霍尔法代码 (递归) 挖坑法 流程及其展开图 代码 (递归) 前后指针法 前后指针法的步骤及其动图 代码(递归) 快排的优化 一、三数取中 二、…

CentOS 7安装MySQL及常见问题与解决方案(含JDBC示例与错误处理)

引言 MySQL是一个流行的开源关系型数据库管理系统&#xff0c;广泛应用于各种业务场景。在CentOS 7上安装MySQL后&#xff0c;我们通常需要使用JDBC&#xff08;Java Database Connectivity&#xff09;连接MySQL进行后端操作。 目录 引言 CentOS 7安装MySQL 使用JDBC连接My…

什么时候去检测大数据信用风险比较合适?

什么时候去检测大数据信用风险比较合适?在当今这个数据驱动的时代&#xff0c;大数据信用风险检测已经成为个人的一项重要需求。本文将从贷前检测、信息泄露检测和定期检测三个方面&#xff0c;阐述何时进行大数据信用风险检测较为合适。 一、贷前检测 大数据信用风险检测在贷…