Spring中实现策略模式示例

news/发布时间2024/5/15 12:22:59

Spring中实现策略模式示例

在本教程中,将探索 Spring 框架中的各种策略模式实现,例如列表注入、映射注入和方法注入。

什么是策略模式?
策略模式是一种设计原则,允许您在运行时切换不同的算法或行为。它允许您在不改变应用程序核心逻辑的情况下插入不同的策略,从而使您的代码具有灵活性和适应性。

这种方法适用于为特定功能任务提供不同实现方式,并使系统更能适应变化的情况。它通过将算法细节与应用程序的主要逻辑分离,促进了更模块化的代码结构。

步骤 1:实施策略
把自己想象成一个黑暗巫师,努力与春天一起掌握不可饶恕诅咒的力量。我们的任务是实现所有三种诅咒--Avada Kedavra、Crucio 和 Imperio。之后,我们将在运行时切换不同的诅咒(策略)。

让我们从策略接口开始:

public interface CurseStrategy {  

    String useCurse();

    String curseName();
}

下一步,我们需要执行所有 "不可饶恕的诅咒":

@Component  
public class CruciatusCurseStrategy implements CurseStrategy {  

@Override  
public String useCurse() {  
return "Attack with Crucio!";  
}  

@Override  
public String curseName() {  
return "Crucio";  
}  
}

@Component  
public class ImperiusCurseStrategy implements CurseStrategy {  

@Override  
public String useCurse() {  
return "Attack with Imperio!";  
}  

@Override  
public String curseName() {  
return "Imperio";  
}  
}

@Component  
public class KillingCurseStrategy implements CurseStrategy {  

@Override  
public String useCurse() {  
return "Attack with Avada Kedavra!";  
}  

@Override  
public String curseName() {  
return "Avada Kedavra";  
}  
}

第 2 步:将诅咒注入 List
Spring 提供了一个神奇的功能,允许我们以 List 的形式注入一个接口的多个实现,这样我们就可以用它来注入策略并在它们之间切换。

但让我们先创建基础:Wizard接口。

public interface Wizard {  
String castCurse(String name); 
}

我们可以在向导中注入我们的诅咒(策略),并筛选出所需的诅咒。

@Service  
public class DarkArtsWizard implements Wizard {  

private final List curses;  

public DarkArtsListWizard(List curses) {  
this.curses = curses;  
}  

@Override  
public String castCurse(String name) {  
return curses.stream()  
.filter(s -> name.equals(s.curseName()))  
.findFirst()  
.orElseThrow(UnsupportedCurseException:: new)  
.useCurse();  
}  
}

如果请求的诅咒不存在,也会产生 UnsupportedCurseException。

public class UnsupportedCurseException extends RuntimeException {  
}

测试
我们可以验证诅咒施放是否有效:

@SpringBootTest  
class DarkArtsWizardTest {  

@Autowired  
private DarkArtsWizard wizard;  

@Test  
public void castCurseCrucio() {  
assertEquals("Attack with Crucio!", wizard.castCurse("Crucio"));  
}  

@Test  
public void castCurseImperio() {  
assertEquals("Attack with Imperio!", wizard.castCurse("Imperio"));  
}  

@Test  
public void castCurseAvadaKedavra() {  
assertEquals("Attack with Avada Kedavra!", wizard.castCurse("Avada Kedavra"));  
}  

@Test  
public void castCurseExpelliarmus() {  
assertThrows(UnsupportedCurseException.class, () -> wizard.castCurse("Abrakadabra"));  
}  
}

另一种流行的方法是定义 canUse 方法,而不是 curseName。这将返回布尔值,并允许我们使用更复杂的过滤功能,例如

public interface CurseStrategy {  

    String useCurse();

    boolean canUse(String name, String wizardType);
}

@Component  
public class CruciatusCurseStrategy implements CurseStrategy {  

@Override  
public String useCurse() {  
return "Attack with Crucio!";  
}  

@Override  
public boolean canUse(String name, String wizardType) {  
return "Crucio".equals(name) && "Dark".equals(wizardType);  
}  
}

@Service  
public class DarkArtstWizard implements Wizard {  

private final List curses;  

public DarkArtsListWizard(List curses) {  
this.curses = curses;  
}  

@Override  
public String castCurse(String name) {  
return curses.stream()  
.filter(s -> s.canUse(name, "Dark")))  
.findFirst()  
.orElseThrow(UnsupportedCurseException:: new)  
.useCurse();  
}  
}

步骤 3:将策略注入Map
我们可以轻松解决上一节中的弊端。Spring 允许我们将 Bean 名称和实例注入 Map。它简化了代码并提高了效率。

@Service  
public class DarkArtsWizard implements Wizard {  

private final Map<String, CurseStrategy> curses;  

public DarkArtsMapWizard(Map<String, CurseStrategy> curses) {  
this.curses = curses;  
}  

@Override  
public String castCurse(String name) {  
CurseStrategy curse = curses.get(name);  
if (curse == null) {  
throw new UnsupportedCurseException();  
}  
return curse.useCurse();  
}  
}

这种方法有一个缺点:Spring 会注入 Bean 名称作为 Map 的键,因此策略名称与 Bean 名称相同,如 cruciatusCurseStrategy。如果 Spring 的代码或我们的类名在未通知的情况下发生变化,这种对 Spring 内部 Bean 名称的依赖可能会导致问题。

让我们检查一下,我们是否仍能施放这些诅咒:

@SpringBootTest  
class DarkArtsWizardTest {  

@Autowired  
private DarkArtsWizard wizard;  

@Test  
public void castCurseCrucio() {  
assertEquals("Attack with Crucio!", wizard.castCurse("cruciatusCurseStrategy"));  
}  

@Test  
public void castCurseImperio() {  
assertEquals("Attack with Imperio!", wizard.castCurse("imperiusCurseStrategy"));  
}  

@Test  
public void castCurseAvadaKedavra() {  
assertEquals("Attack with Avada Kedavra!", wizard.castCurse("killingCurseStrategy"));  
}  

@Test  
public void castCurseExpelliarmus() {  
assertThrows(UnsupportedCurseException.class, () -> wizard.castCurse("Crucio"));  
}  
}

  • 优点:无循环。
  • 缺点:依赖于 Bean 名称,这使得代码的可维护性较差,并且在名称更改或重构时更容易出错。

步骤 4:注入 List 并将其转换为 Map
如果我们注入 List 并将其转换为 Map,就可以轻松消除 Map 注入的弊端:

@Service  
public class DarkArtsWizard implements Wizard {  

private final Map<String, CurseStrategy> curses;  

public DarkArtsMapWizard(List curses) {  
this.curses = curses.stream()  
.collect(Collectors.toMap(CurseStrategy::curseName, Function.identity()));
}  

@Override  
public String castCurse(String name) {  
CurseStrategy curse = curses.get(name);  
if (curse == null) {  
throw new UnsupportedCurseException();  
}  
return curse.useCurse();  
}  
}

有了这种方法,我们就可以使用 curseName 代替 Spring 的 Bean 名称作为 Map 键(策略名称)。

步骤 5:接口中的 @Autowire
Spring 支持在方法中自动布线。自动连接到方法的简单示例是通过设置器注入。此功能允许我们在接口的默认方法中使用 @Autowired,这样我们就可以在向导接口中注册每个 CurseStrategy,而无需在每个策略实现中实现注册方法。

让我们通过添加 registerCurse 方法来更新Wizard接口:

public interface Wizard {  

String castCurse(String name);  

void registerCurse(String curseName, CurseStrategy curse)
}
@Service  
public class DarkArtsWizard implements Wizard {  

private final Map<String, CurseStrategy> curses = new HashMap<>();  

@Override  
public String castCurse(String name) {  
CurseStrategy curse = curses.get(name);  
if (curse == null) {  
throw new UnsupportedCurseException();  
}  
return curse.useCurse();  
}  

@Override  
public void registerCurse(String curseName, CurseStrategy curse) {  
curses.put(curseName, curse);  
}  
}

现在,让我们通过添加带有 @Autowired 注解的方法来更新 CurseStrategy 接口:

public interface CurseStrategy {  

String useCurse();  

String curseName();  

@Autowired  
default void registerMe(Wizard wizard) {  
wizard.registerCurse(curseName(), this);  
}  
}

在注入依赖项的同时,我们将诅咒注册到向导中。

  • 优点:没有循环,也不依赖内部 Spring Bean 名称。
  • 缺点:没有缺点,纯粹的黑魔法。

结论
在本文中,我们探讨了 Spring 环境中的策略模式。我们评估了不同的策略注入方法,并演示了使用 Spring 功能的优化解决方案。

原文

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

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

相关文章

[HTML]Web前端开发技术29(HTML5、CSS3、JavaScript )JavaScript基础——喵喵画网页

希望你开心,希望你健康,希望你幸福,希望你点赞! 最后的最后,关注喵,关注喵,关注喵,佬佬会看到更多有趣的博客哦!!! 喵喵喵,你对我真的很重要! 目录 前言 上一节的课后练习

专业130+总分410+上海交通大学819信号系统与信号处理考研上交电子信息通信生医电科,真题,大纲,参考书。

今年考研顺利结束&#xff0c;我也完成了目前人生最大的逆袭&#xff0c;跨了两个层级跨入c9&#xff0c;专业课819信号系统与信息处理135&#xff0c;数一130总分410&#xff0c;考上上海交大&#xff0c;回想这一年经历了很多&#xff0c;也成长了很多。从周围朋友&#xff0…

CogFixtureTool(坐标系、校正与定位)

坐标系 任何VisionPro图像都支持一组坐标空间&#xff0c;为表达特定特征的位置提供数字框架。最有用的空间是根空间和用户空间&#xff0c;根空间将点与原始获取图像中的像素相关联&#xff0c;用户空间用于获得校准和固定空间中的特征位置和测量值。 根空间 图像的根空间…

springcloud:2.OpenFeign 详细讲解

OpenFeign 是一个基于 Netflix 的 Feign 库进行扩展的工具,它简化了开发人员在微服务架构中进行服务间通信的流程,使得编写和维护 RESTful API 客户端变得更加简单和高效。作为一种声明式的 HTTP 客户端,OpenFeign 提供了直观的注解驱动方式,使得开发人员可以轻松定义和调用…

力扣日记2.22-【回溯算法篇】47. 全排列 II

力扣日记&#xff1a;【回溯算法篇】47. 全排列 II 日期&#xff1a;2023.2.22 参考&#xff1a;代码随想录、力扣 47. 全排列 II 题目描述 难度&#xff1a;中等 给定一个可包含重复数字的序列 nums &#xff0c;按任意顺序 返回所有不重复的全排列。 示例 1&#xff1a; 输…

【Android安全】Windows 环境下载 Android 源码

准备环境 安装 git 安装 Python 硬盘剩余容量最好大于 100G 打开 Git Bash&#xff0c;用 git 克隆源代码仓库 git clone https://android.googlesource.com/platform/manifest.git //没有梯子使用清华源 git clone https://aosp.tuna.tsinghua.edu.cn/platform/manifest.git…

AutoTimes:通过大语言模型的自回归时间序列预测器

论文标题&#xff1a; AutoTimes: Autoregressive Time Series Forecasters via Large Language Models 作者&#xff1a;Yong Liu, Guo Qin, Xiangdong Huang, Jianmin Wang, Mingsheng Long 链接&#xff1a;https://arxiv.org/abs/2402.02370 机构&#xff1a;清华大学 …

【NTN 卫星通信】基于NTN和TN的Inter-PLMN海事应用场景

1 场景概述 NTN和TN联合组网的场景&#xff0c;可以有多种应用方式&#xff0c;以下用例描述了同时使用多个卫星PLMN和一个地面5G PLMN的海事场景。 MNO-G是一家成熟的卫星PLMN运营商&#xff0c;运营着几颗GEO卫星。MNO-L是一个相对较新的卫星PLMN运营商&#xff0c;操作LEO卫…

挑战30天学完Python:Day16 日期时间

&#x1f4d8; Day 16 &#x1f389; 本系列为Python基础学习&#xff0c;原稿来源于 30-Days-Of-Python 英文项目&#xff0c;大奇主要是对其本地化翻译、逐条验证和补充&#xff0c;想通过30天完成正儿八经的系统化实践。此系列适合零基础同学&#xff0c;或仅了解Python一点…

Android 输入法框架简介

每种平台都有自己的输入法框架. GNU/Linux 桌面环境有多种输入法框架, 比如 ibus, fcitx 等. 但是 Android 操作系统只有一种, 是统一提供的输入法框架. 相关链接: 《ibus 源代码阅读 (1)》 https://blog.csdn.net/secext2022/article/details/136099328https://developer.and…

【大厂AI课学习笔记NO.51】2.3深度学习开发任务实例(4)计算机视觉实际应用的特点

今天考试通过腾讯云人工智能从业者TCA级别的认证了&#xff01; 还是很开心的&#xff0c;也看不到什么更好的方向&#xff0c;把一切能利用的时间用来学习&#xff0c;总是对的。 我把自己考试通过的学习笔记&#xff0c;都分享到这里了&#xff0c;另外还有一个比较全的思维…

跨越千年医学对话:用AI技术解锁中医古籍知识,构建能够精准问答的智能语言模型,成就专业级古籍解读助手(LLAMA)

跨越千年医学对话&#xff1a;用AI技术解锁中医古籍知识&#xff0c;构建能够精准问答的智能语言模型&#xff0c;成就专业级古籍解读助手&#xff08;LLAMA&#xff09;。 介绍&#xff1a;首先在 Ziya-LLaMA-13B-V1基线模型的基础上加入中医教材、中医各类网站数据等语料库&a…

【爬虫逆向实战篇】定位加密参数、断点调试与JS代码分析

文章目录 1. 写在前面2. 确认加密参数3. 加密参数定位4. XHR断点调试 【作者主页】&#xff1a;吴秋霖 【作者介绍】&#xff1a;Python领域优质创作者、阿里云博客专家、华为云享专家。长期致力于Python与爬虫领域研究与开发工作&#xff01; 【作者推荐】&#xff1a;对JS逆向…

爬取m3u8视频

网址&#xff1a;https://www.bhlsm.com/cupfoxplay/609-3-1/ 相关代码&#xff1a; #采集网址&#xff1a;https://www.bhlsm.com/cupfoxplay/609-3-1/ #正常视频网站&#xff1a;完整视频内容 # pip install pycryptodomex #流媒体文件&#xff1a;M3U8&#xff08;把完整的…

React18源码: React调度中的3种优先级类型和Lane的位运算

优先级类型 React内部对于优先级的管理&#xff0c;贯穿运作流程的4个阶段&#xff08;从输入到输出&#xff09;&#xff0c;根据其功能的不同&#xff0c;可以分为3种类型&#xff1a; 1 &#xff09;fiber优先级(LanePriority) 位于 react-reconciler包&#xff0c;也就是L…

[ROS 系列学习教程] rosbag 命令行介绍

ROS 系列学习教程(总目录) 本文目录 rosbag 命令行1.1 rosbag check1.2 rosbag compress1.3 rosbag decompress1.4 rosbag filter1.5 rosbag fix1.6 rosbag info1.7 rosbag play1.8 rosbag record1.9 rosbag reindex 有时我们需要将 topic 中的数据保存下来以便后面分析&#x…

西门子S7-1500作为智能设备共享功能

本章节介绍了共享设备的功能&#xff0c;优势&#xff0c;使用要求&#xff0c;使用规则&#xff0c;如何将智能设备作为共享设备&#xff0c;实现一个智能设备同时与2个IO控制器进行通信的示例&#xff0c;以及常见问题。 一、共享设备功能概述 信号模块可以被不同的IO控制器…

SpringMVC 学习(三)之 @RequestMapping 注解

目录 1 RequestMapping 注解介绍 2 RequestMapping 注解的位置 3 RequestMapping 注解的 value 属性 4 RequestMapping 注解的 method 属性 5 RequestMapping 注解的 params 属性&#xff08;了解&#xff09; 6 RequestMapping 注解的 headers 属性&#xff08;了解&…

Linux环境下基本指令

今天我们一起来认识一下Linux环境下一些基本的指令&#xff0c;这些指令是我们学习Linux的基础&#xff0c;只有掌握了这些指令&#xff0c;我们才能在Linux环境下进一步学习知识&#xff0c;话不多说&#xff0c;我们开始&#xff08;以下演示操作是在云服务器的环境下&#x…

k8s-hpa控制器 16

hpa可通过metrics-server所提供pod的cpu或者内存的负载情况&#xff0c;从而动态拉伸控制器的副本数&#xff0c;从而达到后端的自动弹缩 官网&#xff1a;https://kubernetes.io/zh/docs/tasks/run-application/horizontal-pod-autoscalewalkthrough/ 上传镜像 创建hpa实例 …
推荐文章