【设计模式】【行为型模式】策略模式(Strategy)

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
📫 欢迎+V: flzjcsg2,我们共同讨论Java深渊的奥秘
🎵 当你的天空突然下了大雨,那是我在为你炸乌云

文章目录

  • 一、入门
    • 什么是策略模式?
    • 为什么需要策略模式?
    • 怎样实现策略模式?
  • 二、策略模式在源码中的运用
    • 2.1、Java Collections 中的排序策略
    • 2.2、Spring 中的资源加载策略
  • 三、总结
  • 参考

一、入门

什么是策略模式?

策略模式是一种行为设计模式,允许在运行时选择算法或行为。它将算法封装在独立的类中,使得它们可以互换,而不影响客户端代码。

为什么需要策略模式?

策略模式的主要目的是解决算法或行为在代码中硬编码的问题,使得系统更加灵活、可扩展和易于维护。可以优化大量的if-else。

怎样实现策略模式?

策略模式的主要角色如下:
抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略所需的接口。
具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为
环境(Context)类:持有一个策略类的引用,最终给客户端调用。

【案例】促销活动
百货公司,在不同的节日,会有不同的促销
在这里插入图片描述

定义百货公司所有促销活动的共同接口

public interface Strategy {void show();
}

定义具体策略角色(Concrete Strategy):每个节日具体的促销活动

//为春节准备的促销活动A
public class StrategyA implements Strategy {public void show() {System.out.println("买一送一");}
}
//为中秋准备的促销活动B
public class StrategyB implements Strategy {public void show() {System.out.println("满200元减50元");}
}
//为圣诞准备的促销活动C
public class StrategyC implements Strategy {public void show() {System.out.println("满1000元加一元换购任意200元以下商品");}
}
定义环境角色(Context):用于连接上下文,即把促销活动推销给客户,这里可以理解为销售员
public class SalesMan {            //持有抽象策略角色的引用               private Strategy strategy;        public SalesMan(Strategy strategy) {   this.strategy = strategy;       }                     //向客户展示促销活动                public void salesManShow(){        strategy.show();           }                     
} 

二、策略模式在源码中的运用

2.1、Java Collections 中的排序策略

Java的Collections.sort()方法使用了策略模式来实现排序功能。它允许通过传递不同的Comparator实现来定义不同的排序策略。

List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9);// 策略1:升序排序
Collections.sort(numbers, new Comparator<Integer>() {@Overridepublic int compare(Integer a, Integer b) {return a.compareTo(b);}
});
System.out.println("升序排序: " + numbers);// 策略2:降序排序
Collections.sort(numbers, new Comparator<Integer>() {@Overridepublic int compare(Integer a, Integer b) {return b.compareTo(a);}
});
System.out.println("降序排序: " + numbers);

在上面的代码中Comparator是策略接口。具体的排序逻辑(升序、降序)是策略实现。Collections.sort()是上下文,负责调用策略。
源码中,Collections类的sort方法,里面调用了Arrays类的sort方法。ArraysTimSort类的sort方法

// Collections类
public static <T> void sort(List<T> list, Comparator<? super T> c) {list.sort(c);
}default void sort(Comparator<? super E> c) {Object[] a = this.toArray();Arrays.sort(a, (Comparator) c);    // 调用Arrays类的sort方法ListIterator<E> i = this.listIterator();for (Object e : a) {i.next();i.set((E) e);}
}// Arrays类
public static <T> void sort(T[] a, Comparator<? super T> c) {if (c == null) {sort(a);} else {if (LegacyMergeSort.userRequested)legacyMergeSort(a, c);elseTimSort.sort(a, 0, a.length, c, null, 0, 0); // 调用TimSort类的sort方法}
}

TimSort类中,这里我们只要关注,我们传的策略,入参c的使用地方就好了。这里会调用countRunAndMakeAscending方法,我们关注这个方法,我们传的排序策略,也就是入参c,会被使用。

static <T> void sort(T[] a, int lo, int hi, Comparator<? super T> c,T[] work, int workBase, int workLen) {assert c != null && a != null && lo >= 0 && lo <= hi && hi <= a.length;int nRemaining  = hi - lo;if (nRemaining < 2)return;  // Arrays of size 0 and 1 are always sorted// If array is small, do a "mini-TimSort" with no mergesif (nRemaining < MIN_MERGE) {int initRunLen = countRunAndMakeAscending(a, lo, hi, c);  // 关注调用这个方法
...// countRunAndMakeAscending 方法
private static <T> int countRunAndMakeAscending(T[] a, int lo, int hi,Comparator<? super T> c) {assert lo < hi;int runHi = lo + 1;if (runHi == hi)return 1;if (c.compare(a[runHi++], a[lo]) < 0) { // 排序策略被使用while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)runHi++;reverseRange(a, lo, runHi);} else {                          while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)runHi++;}return runHi - lo;
}

2.2、Spring 中的资源加载策略

Spring框架中的ResourceLoader接口和其实现类(如ClassPathResourceLoaderFileSystemResourceLoader等)也使用了策略模式。

ResourceLoader resourceLoader = new DefaultResourceLoader();// 策略1:从类路径加载资源
Resource classPathResource = resourceLoader.getResource("classpath:application.properties");
System.out.println("类路径资源: " + classPathResource.exists());// 策略2:从文件系统加载资源
Resource fileSystemResource = resourceLoader.getResource("file:/path/to/file.txt");
System.out.println("文件系统资源: " + fileSystemResource.exists());

ResourceLoader是策略接口。具体的资源加载逻辑(类路径、文件系统等)是策略实现。DefaultResourceLoader是上下文,负责调用策略。
下面是结合源码说明
策略接口:ResourceLoader 接口

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

具体策略:ClassPathResource、FileSystemResource等是具体的策略实现。

// ClassPathResource实现
public class ClassPathResource extends AbstractFileResolvingResource {private final String path;public ClassPathResource(String path) {this.path = path;}@Overridepublic InputStream getInputStream() throws IOException {InputStream is = getClassLoader().getResourceAsStream(path);if (is == null) {throw new FileNotFoundException("Resource not found: " + path);}return is;}
}// FileSystemResource
public class FileSystemResource extends AbstractResource {private final File file;public FileSystemResource(String path) {this.file = new File(path);}@Overridepublic InputStream getInputStream() throws IOException {return new FileInputStream(file);}
}

上下文:DefaultResourceLoader 是上下文,负责根据路径选择合适的策略。

public class DefaultResourceLoader implements ResourceLoader {@Overridepublic Resource getResource(String location) {// 根据路径前缀选择策略if (location.startsWith(CLASSPATH_URL_PREFIX)) {return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()));}else if (location.startsWith(FILE_URL_PREFIX)) {return new FileSystemResource(location.substring(FILE_URL_PREFIX.length()));}else {try {// 尝试作为URL加载URL url = new URL(location);return new UrlResource(url);}catch (MalformedURLException ex) {// 如果都不是,默认为文件系统资源return new FileSystemResource(location);}}}
}

这里应该有UU们会好奇,诶,为什么具体策略没有实现策略接口?Spring 的资源加载策略是策略模式的一种变体。它与经典策略模式的区别在于:

  • 经典策略模式:
    • 策略接口和具体策略是直接实现的。
    • 例如,排序策略中,Comparator 是策略接口,具体的排序类是策略实现。
  • Spring 的资源加载策略:
    • 策略接口(ResourceLoader)和具体策略(ClassPathResource 等)之间通过上下文(DefaultResourceLoader)连接。
    • 具体策略实现的是 Resource 接口,而不是 ResourceLoader 接口。

三、总结

策略模式通过将算法或行为封装到独立的类中,提供了一种灵活、可扩展的方式来管理代码中的变化部分。它的核心优势是解耦动态切换,但也会带来类的数量增加和客户端使用成本的问题。适用于需要动态切换行为、避免重复代码或隔离算法实现细节的场景。

优点
灵活性:允许在运行时动态切换算法或行为,无需修改客户端代码。
可扩展性:新增策略时只需添加新的策略类,符合开闭原则(对扩展开放,对修改关闭)。
解耦:将算法或行为与使用它的上下文分离,降低了代码的耦合度。
避免重复代码:将相似的算法提取到独立的策略类中,减少代码重复。
易于测试:每个策略类可以独立测试,简化了测试过程。

缺点
增加类的数量:每个策略都需要一个独立的类,可能会导致类的数量增多,增加系统复杂性。
客户端需要了解策略:客户端必须知道有哪些策略,并选择合适的策略,增加了使用成本。
性能开销:在运行时切换策略可能会引入额外的性能开销(如对象创建和销毁)

适用场景
需要动态切换算法或行为:例如,支付方式、排序算法、资源加载策略等。
有多个相似的类,只有行为不同:例如,不同类型的折扣计算、不同的日志记录方式等。
避免使用复杂的条件语句:当代码中有大量if-else或switch-case语句时,可以用策略模式替代。
需要隔离算法的实现细节:当不希望暴露算法的实现细节,或者希望算法可以独立变化时。
需要对算法进行扩展:当系统需要支持新的算法,且不希望修改现有代码时。

参考

黑马程序员Java设计模式详解, 23种Java设计模式(图解+框架源码分析+实战)_哔哩哔哩_bilibili

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

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

相关文章

【高级架构师】多线程和高并发编程(三):锁(中)深入ReentrantLock

文章目录 3、深入ReentrantLock3.1 ReentrantLock和synchronized的区别3.2 AQS概述3.3 加锁流程源码剖析3.3.1 加锁流程概述3.3.2 三种加锁源码分析3.3.2.1 lock方法3.3.2.2 tryLock方法3.3.2.3 lockInterruptibly方法 3.4 释放锁流程源码剖析3.4.1 释放锁流程概述3.4.2 释放锁…

WPF 进度条(ProgressBar)示例一

本文讲述&#xff1a;WPF 进度条(ProgressBar)简单的样式修改和使用。 进度显示界面&#xff1a;使用UserControl把ProgressBar和进度值以及要显示的内容全部组装在UserControl界面中&#xff0c;方便其他界面直接进行使用。 <UserControl x:Class"DefProcessBarDemo…

Android studio怎么创建assets目录

在Android Studio中创建assets文件夹是一个简单的步骤&#xff0c;通常用于存储不需要编译的资源文件&#xff0c;如文本文件、图片、音频等 main文件夹&#xff0c;邮件new->folder-assets folder

工业相机在工业生产制造过程中的视觉检测技术应用

随着技术不断发展以及工业4.0时代的到来&#xff0c;利用工业相机进行视觉检测技术已经成为制造业不可或缺的一部分。通过结合先进的计算机视觉、AI算法和自动化设备&#xff0c;工业视觉检测为生产线质量控制和效率提升提供了革命性的解决方案。 一、什么是工业视觉检测技术 …

快速上手Vim的使用

Vim Linux编辑器-vim使用命令行模式下所有选项都可以带数字底行模式可视块模式&#xff08;ctrlV进入&#xff09; Linux编辑器-vim使用 Vim有多种模式的编辑器。能帮助我们很快的进行代码的编辑&#xff0c;甚至完成很多其他事情。 默认情况下我们打开vim在命令模式下&#x…

数据结构-基础

1、概念&#xff1a; 程序 数据结构 算法 2、程序的好坏 可读性&#xff0c;稳定性&#xff0c;扩展性&#xff0c;时间复杂度&#xff0c;空间复杂度。 3、数据结构 是指存储、组织数据的方式&#xff0c;以便高效地进行访问和修改。通过选择适当的数据结构&#xff0c; 能…

本地部署DeepSeek(Mac版本,带图形化操作界面)

一、下载安装&#xff1a;Ollama 官网下载&#xff1a;Download Ollama on macOS 二、安装Ollama 1、直接解压zip压缩包&#xff0c;解压出来就是应用程序 2、直接将Ollama拖到应用程序中即可 3、启动终端命令验证 # 输入 ollama 代表已经安装成功。 4、下载模型 点击模型…

山东大学软件学院人机交互期末复习笔记

文章目录 2022-2023 数媒方向2023-2024 软工方向重点题目绪论发展阶段 感知和认知基础视觉听觉肤觉知觉认知过程和交互设计原则感知和识别注意记忆问题解决语言处理影响认知的因素 立体显示技术及其应用红蓝眼镜偏振式眼镜主动式&#xff08;快门时&#xff09;立体眼镜 交互设…

《Kettle实操案例一(全量/增量更新与邮件发送)》

目录 一、场景描述:二、要求:三、思路四、整体作业五、各部分详细配置1、Start2、转换-获取执行开始时间3、获取目标表抽取前行数4、检验字段的值5、增量更新6、全量更新7、获取目标表抽取后行数8、获取执行结束时间9、日志写入数据库10、写日志11、发送数据抽取完成邮件 六、最…

位运算算法篇:进入位运算的世界

位运算算法篇&#xff1a;进入位运算的世界 本篇文章是我们位运算算法篇的第一章&#xff0c;那么在我们是算法世界中&#xff0c;有那么多重要以及有趣的算法&#xff0c;比如深度优先搜索算法以及BFS以及动态规划算法等等&#xff0c;那么我们位运算在这些算法面前相比&#…

redis高级数据结构HyperLogLog

文章目录 背景常见API注意事项实现原理1、哈希函数2、前导零统计3、存储与计数4、基数估算 pf 的内存占用为什么是 12k&#xff1f;总结 背景 在开始这一节之前&#xff0c;我们先思考一个常见的业务问题&#xff1a;如果你负责开发维护一个大型的网站&#xff0c;有一天老板找…

<tauri><rust><GUI>基于rust和tauri,在已有的前端框架上手动集成tauri示例

前言 本文是基于rust和tauri&#xff0c;由于tauri是前、后端结合的GUI框架&#xff0c;既可以直接生成包含前端代码的文件&#xff0c;也可以在已有的前端项目上集成tauri框架&#xff0c;将前端页面化为桌面GUI。 环境配置 系统&#xff1a;windows 10 平台&#xff1a;visu…

mysql 学习11 事务,事务简介,事务操作,事务四大特性,并发事务问题,事务隔离级别

一 事务简介&#xff0c; 数据库准备&#xff1a; create table account(id int auto_increment primary key comment 主键ID,name varchar(128) not null comment 姓名,backaccountnumber char(18) unique comment 银行账号,money float comment 余额 )comment 银行账号表;…

重塑生产制造企业项目管理新范式:项目模板在Tita中的卓越实践

在竞争激烈的生产制造领域&#xff0c;每一个项目的成功执行都是企业稳健前行的重要基石。然而&#xff0c;面对复杂多变的生产流程、严格的交货期限以及不断变化的客户需求&#xff0c;如何确保项目高效、有序地进行&#xff0c;成为了众多企业面临的共同挑战。此时&#xff0…

Ajax-介绍

概念: Asynchronous JavaScript And XML&#xff0c;异步的JavaScript和XML. 作用: 数据交换:通过Aiax可以给服务器发送请求&#xff0c;并获取服务器响应的数据 异步交互: 可以在不重新加载整个页面的情况下&#xff0c;与服务器交换数据并更新部分网页的技术, 如:搜索联想、…

基于可信数据空间的企业数据要素与流通体系建设(附ppt 下载)

近期&#xff0c;可信数据空间会议召开。大数据系统软件国家工程研究中心总工程师王晨发表了题为《基于可信数据空间的企业数据要素与流通体系建设》主旨演讲。 WeChat Subscription Account【智慧城市指北】&#xff0c;可搜索相关关键字“20250107”&#xff0c;可获取具体获…

idea整合deepseek实现AI辅助编程

1.File->Settings 2.安装插件codegpt 3.注册deepseek开发者账号&#xff0c;DeepSeek开放平台 4.按下图指示创建API KEY 5.回到idea配置api信息&#xff0c;File->Settings->Tools->CodeGPT->Providers->Custom OpenAI API key填写deepseek的api key Chat…

Composo:企业级AI应用的质量守门员

在当今快速发展的科技世界中,人工智能(AI)的应用已渗透到各行各业。然而,随着AI技术的普及,如何确保其可靠性和一致性成为了企业面临的一大挑战。Composo作为一家致力于为企业提供精准AI评估服务的初创公司,通过无代码和API双模式,帮助企业监测大型语言模型(LLM)驱动的…

增加工作台菜单页面,AI问答应用支持上下文设置,数据库表索引优化,zyplayer-doc 2.4.8 发布啦!

zyplayer-doc是一款适合企业和个人使用的WIKI知识库管理工具&#xff0c;支持在线编辑富文本、Markdown、表格、Office文档、API接口、思维导图、Drawio以及任意的文本文件&#xff0c;专为私有化部署而设计&#xff0c;最大程度上保证企业或个人的数据安全&#xff0c;支持以内…

C++开发(软件开发)常见面试题

目录 1、C里指针和数组的区别 2、C中空指针请使用nullptr不要使用NULL 3、http/https区别和头部结构&#xff1f; 4、有了mac地址为什么还要ip地址&#xff1f;ip地址的作用 5、有了路由器为什么还要交换机&#xff1f; 6、面向对象三大特性 7、友元函数 8、大端小端 …