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

策略模式

  • 一、介绍
  • 二、不同的支付方式采用不同的策略
  • 三、 电商定价策略
  • 四、日志记录策略
  • 五、 压缩算法
  • 六、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.mzph.cn/news/743631.shtml

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

相关文章

计算机行业在数字经济时代的角色与数字化转型之路

目录 前言1 数字经济时代下的计算机行业角色与定位1.1 数字经济支撑者1.2 创新引领者1.3 产业融合者 2 数字化转型对计算机行业的影响与挑战2.1 技术更新换代的压力2.2 人才培养与流动的问题2.3 数据隐私与安全的挑战 3 数字化转型如何提升行业竞争力3.1 提高生产效率与优化产品…

【Java】获取手机文件名称补充

本地的 ADB 工具路径指的是你电脑上安装的 Android Debug Bridge&#xff08;ADB&#xff09;工具的路径。ADB 是 Android SDK 中的一个工具&#xff0c;用于与连接到计算机上的 Android 设备进行通信。你需要确保 ADB 已正确安装&#xff0c;并知道其在你计算机上的位置。 通…

Prometheus 监控告警配置

文章目录 一、告警通知1.邮件通知2.钉钉通知2.1.获取钉钉机器人webhook2.2.prometheus-webhook-dingtalk2.3.配置信息2.4.自定义模板 3.自定义 二、告警规则1.Prometheus2.Linux3.Docker4.Nginx5.Redis6.PostgreSQL7.MySQL8.RabbitMQ9.JVM10.Elasticsearch 开源中间件 # Prome…

odoo中使用domain基础操作

Odoo中的domain是一个用于筛选记录的条件列表&#xff0c;它通常用于搜索、过滤和定义视图中可见记录的规则。Domain由一系列的元组组成&#xff0c;每个元组定义了一个筛选条件。这些条件可以是简单的字段比较&#xff0c;也可以是更复杂的逻辑组合。 基本语法 Domain的基础…

模拟无人驾驶汽

下面是一个简单的Python代码示例&#xff0c;用于模拟无人驾驶汽车的自动驾驶功能。 import timeclass Car:def __init__(self):self.speed 0self.heading 0def drive(self):while True:self.update_sensors()self.process_sensors()self.control_car()self.update_car()tim…

CentOS搭建NAS服务器并使用

CentOS搭建NAS服务器并使用 文章目录 前言一、配置NAS服务器安装 NFS 服务&#xff1a;启动 NFS 服务&#xff1a;使 NFS 服务在系统启动时自动启动&#xff1a; 二、挂载服务器三、常见错误以及解决方案1、mount.nfs: No route to host2、mount.nfs: access denied by server …

vue3之组合式函数

抽取成一个组合式函数&#xff1a; // fetch.js //接收响应式状态 import { ref, watchEffect, toValue } from vue //一个封装的异步请求 import { fetch } from ../XX export function useFetch(url) {const data ref(null)const error ref(null)const fetchData () >…

OpenCV的常用数据类型

OpenCV涉及的常用数据类型除包含C的基本数据类型,如&#xff1a;char、uchar&#xff0c;int、unsigned int,short 、long、float、double等数据类型外, 还包含Vec&#xff0c;Point、Scalar、Size、Rect、RotatedRect、Mat等类。C中的基本数据类型不需再做说明下面重点介绍一下…

打包 加載AB包 webGl TextMeshPro 變紫色的原因

1.打包 加載AB包 webGl TextMeshPro 變紫色的原因 編輯器命令行https://docs.unity3d.com/cn/2019.4/Manual/CommandLineArguments.html 1.UnityHub 切換命令行參數 -force-gles 2.-force-gles&#xff08;仅限 Windows&#xff09;| 使 Editor 使用 OpenGL for Embedded Sys…

揭秘WMM:wifi中的QOS

更多内容在 WiFi WMM&#xff08;无线多媒体&#xff09;是一种用于无线局域网&#xff08;WLAN&#xff09;的QoS&#xff08;服务质量&#xff09;标准。WMM旨在提供更好的网络性能&#xff0c;特别是在传输多媒体内容&#xff08;如音频和视频&#xff09;时。它通过对不同类…

42.坑王驾到第八期:uniCloud报错

uniCloud 报错 今天调用云函数来调试小程序的时候突然暴了一个奇葩错误&#xff0c;require(…).main is not a function。翻官方文档后发现&#xff0c;原来是这样&#xff1a;**如果你写的是云对象&#xff0c;入口文件应为 index.obj.js&#xff0c;如果你写的是云函数入口…

python学习2:日志记录的用法

一些日志记录的简单记录&#xff1a; 用basicConfig可以进行配置 注意日志的等级&#xff1a; 上述代码得到的日志如下&#xff08;最基础的日志&#xff09;&#xff1a; 关于记录下来的日志格式可以有很多内容&#xff1a;如等级、发生的时间、发生的位置、发生的进程、…

WinRAR功能之【加密文件名】

很多人知道&#xff0c;WinRAR解压缩软件可以给压缩包设置密码&#xff0c;这样就可以保护压缩包里的文件&#xff0c;不被随意打开。 设置密码后&#xff0c;双击压缩包还是可以打开的&#xff0c;但要打开里面的文件时&#xff0c;就需要输入原本设置的密码才能打开。 虽然…

蓝桥杯-Python组(一)

1. 冒泡排序 算法步骤&#xff1a; 比较相邻元素&#xff0c;如果第一个大于第二个则交换从左往右遍历一遍&#xff0c;重复第一步&#xff0c;可以保证最大的元素在最后面重复上述操作&#xff0c;可以得到第二大、第三大、… n int(input()) a list(map(int, input()…

三、NLP中的句子关系判断

句子关系判断是指判断句子是否相似&#xff0c;是否包含&#xff0c;是否是问答关系等&#xff0c;常应用在文本去重、检索&#xff08;用户输入和文档的相关性&#xff09;、推荐&#xff08;和用户喜好文章是否相似&#xff09;等场景中。 3.0、文本相似度计算 3.0.0 传统机…

Vivado原语模板

1.原语的概念 原语是一种元件! FPGA原语是芯片制造商已经定义好的基本电路元件,是一系列组成逻辑电路的基本单元,FPGA开发者编写逻辑代码时可以调用原语进行底层构建。 2.原语的分类 原语可分为预定义原语和用户自定义原语。预定义原语为如and/or等门级原语不需要例化,可以…

【AIGC调研系列】AIGC企业级模型Command-R介绍

Command-R与其他大语言模型的主要区别在于其专为企业级应用设计&#xff0c;特别是在检索增强生成&#xff08;RAG&#xff09;和工具使用方面。Command-R是一个350亿参数的高性能生成模型&#xff0c;具有开放式权重&#xff0c;能够支持多种用例&#xff0c;包括推理、摘要和…

用C语言链表实现图书管理

#include <stdio.h> #include <stdlib.h> #include <string.h> struct ListNode {int val;//编号char title[50];//书名float price;//价格struct ListNode* next; };// 在尾部插入节点 struct ListNode* insertAtTail(struct ListNode* head, int val,char …

Elastic script_score的使用

script_score介绍 在Elasticsearch中&#xff0c;script_score是在function_score查询中的一种功能强大的方式&#xff0c;允许用户使用内置Painless脚本语言或者其他支持的语言来动态计算每个文档的评分 script_score语法 GET /<索引名>/_search {"query":…

一文了解什么是函数柯里化

前言 柯里化(Currying)和反柯里化(Uncurrying)在JavaScript中总感觉属于一种不温不火的存在&#xff0c;甚至有些开发者在提起柯里化和反柯里化时&#xff0c;竟然会有点生疏不懂。其实不然&#xff0c;对于它们的概念可能在日常开发中不太提到&#xff0c;但是它们的思想和用…