【设计模式】SpringBoot优雅使用策略模式

文章目录

  • 1.概述
    • 1.1.简述策略模式
  • 2.实现方法
    • 2.1.实现思路
    • 2.2.实现代码
    • 2.3.策略拓展
    • 2.4.执行调用
  • 3.总结

1.概述

本篇文章主要会描述SpringBoot与策略模式的结合使用,因为不涉及到理论部分,所以在阅读本篇之前,需要对策略模式的理论已经有了一个基本的了解。

1.1.简述策略模式

策略模式有3种角色,分别为:选择器抽象策略策略实例
其中选择器selector又被称为上下文context,其作用为通过不同的标识来获取对应的策略实例。策略实例就是封装不同算法的实例对象,而抽象策略就是策略实例的顶层接口。

简单类图大概就是这个样子:
在这里插入图片描述

2.实现方法

我们在学习设计模式的时候会发现在各类模式中的类与对象都是手动创建的,而在日常的开发中,我们往往会将对象的生命周期交给Spring管理,也就是说,需要我们自行将各类bean组合成一个可运行的设计模式。

假设我们有这样一个场景,需要对系统的中的数据做统计,需求中的统计维度分为:按周统计按月统计,现使用策略模式来实现这个需求。

2.1.实现思路

  • 前置设计:通过定义常量来标识策略的类型,使用者调用时可以通过常量获取对应的策略实例。
  • 策略设计:按周按月分别对应两个bean实例,在内部各自实现对应的统计维度逻辑,在两个bean实例的上层是抽象策略,有一个通用的接口(或抽象类)用于对外提供访问入口。
  • 选择器设计:可以通过Map来存储数据,调用者调用时可以通过策略标识来获取对应的策略实例。

可以看到,实现思路是比较简单点的,现在的问题就是如何把策略的bean实例对象放到Map中。

最简单的方式当然就是把对应的beanClass对象直接写死在Map中,调用的时候可以通过applicationContext.getBean()获取到bean实例。但是这种方式不利于拓展,后续要新增一个策略实例的时候,还得修改这里的Map

第二种方式,我们可以通过解析注解来实现,给每个策略实例打上一个注解,注解中的值对应的就是按周按月这样的常量标识,在SpringBoot启动时,通过扩展点扫描抽象策略,获取它所有的策略实例,解析注解后放入Map中。这种方式利于拓展,新增一个策略实例不需要对旧代码有任何改动

2.2.实现代码

说了半天,不如直接看一下代码实现。

  • 第一步:定义注解:
    import java.lang.annotation.*;
    
    /**
     * 统计策略注解
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.TYPE})
    @Inherited
    @Documented
    public @interface StatisticStrategyAnno {
        String value();
    }
    
  • 第二步:编写抽象策略与策略实例
    /**
     * 统计抽象策略处理器
     */
    public interface StatisticBaseHandler {
        void doStatistic();
    }
    
    import org.springframework.stereotype.Component;
    
    /**
     * 月统计策略
     */
    @Component
    @StatisticStrategyAnno("month")
    public class StatisticByMonthHandler implements StatisticBaseHandler {
    
        @Override
        public void doStatistic() {
            System.out.println("StatisticByMonthHandler");
        }
    }
    
    import org.springframework.stereotype.Component;
    
    /**
     * 周统计策略
     */
    @Component
    @StatisticStrategyAnno("week")
    public class StatisticByWeekHandler implements StatisticBaseHandler {
    
        @Override
        public void doStatistic() {
            System.out.println("StatisticByWeekHandler");
        }
    }
    
  • 第三步:编写选择器
    import org.jetbrains.annotations.NotNull;
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Component;
    
    import java.util.Map;
    import java.util.stream.Collectors;
    
    /**
     * 统计策略选择器
     */
    @Component
    public class StatisticStrategySelector implements ApplicationContextAware {
    
        private Map<String, StatisticBaseHandler> selectorMap;
    
        /**
         * 根据类型选择对应的策略
         *
         * @param type 统计周期类型
         * @return 统计抽象策略处理器
         */
        public StatisticBaseHandler select(String type) {
            return selectorMap.get(type);
        }
    
        @Override
        public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException {
            // 通过注解获取所有的统计策略处理器,并将其注入到map中
            this.selectorMap = applicationContext.getBeansOfType(StatisticBaseHandler.class).values().stream()
                    .filter(strategy -> strategy.getClass().isAnnotationPresent(StatisticStrategyAnno.class))
                    .collect(Collectors.toMap(strategy -> strategy.getClass().getAnnotation(StatisticStrategyAnno.class).value(), strategy -> strategy));
        }
    }
    

启动完成之后,Map中的值如下图所示。
在这里插入图片描述

2.3.策略拓展

现在需求发生了变化,需要加入一个按年统计的策略类型,只需要新增一个策略实例即可,如下:

import org.springframework.stereotype.Component;

/**
 * 年统计策略
 */
@Component
@StatisticStrategyAnno("year")
public class StatisticByYearHandler implements StatisticBaseHandler {

    @Override
    public void doStatistic() {
        System.out.println("StatisticByYearHandler");
    }
}

其他的代码都不需要修改,再次查看Map中的值:
在这里插入图片描述

2.4.执行调用

随便写了一个测试方法,如下:

public void invoke() {
    this.doInvoke("week");
    this.doInvoke("month");
    this.doInvoke("year");
}

public void doInvoke(String type) {
    StatisticBaseHandler handler = select(type);
    handler.doStatistic();
}

在这里插入图片描述
分别打印出了按周统计按月统计按年统计方法中的输出值,表示策略模式定义成功了,在我们实际的开放中,只需要前端(或其他调用端)传入对用的标识字符串,就可以执行不同的统计逻辑了。

3.总结

通过Spring获取接口的实现,并解析实现类上的注解的方式,可以在程序启动时动态的将策略注入到一个Map中,作为策略的容器。使用时传入标识符就可以获取到对应的策略执行了。

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

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

相关文章

HarmonyOS学习路之开发篇—Java UI框架(JS FA调用Java PA)

JS FA调用Java PA机制 使用兼容JS的类Web开发范式的方舟开发框架提供了JS FA&#xff08;Feature Ability&#xff09;调用Java PA&#xff08;Particle Ability&#xff09;的机制&#xff0c;该机制提供了一种通道来传递方法调用、处理数据返回以及订阅事件上。 当前提供Ab…

鼠标键盘实验

文章目录 USB参考资料USB设备STM32F407USB 硬件连接软件移植官方HIDSTM32F4USB通信库 USB参考资料 ①《STM32F4xx中文参考手册》-第30章 全速USB on-the-go(OTG_FS) ②光盘&#xff1a;STM32参考资料:STM32 USB 学习资料-CD00289278.pdf(UM1021) ③光盘&#xff1a;STM32参考资…

Python3 函数与数据结构 | 菜鸟教程(十一)

目录 一、Python3 函数 &#xff08;一&#xff09;定义一个函数 1、你可以定义一个由自己想要功能的函数&#xff0c;以下是简单的规则&#xff1a; 2、语法 3、实例 ①让我们使用函数来输出"Hello World&#xff01;"&#xff1a; ②更复杂点的应用&#xff…

【gcc, cmake, eigen, opencv,ubuntu】一.gcc介绍

文章目录 gcc介绍1.查看当前gcc 版本2.安装其他版本的gcc3.设置多个版本的优先级4.修改默认的版本5.查看cpu信息 gcc介绍 gcc介绍和makefile介绍 1.查看当前gcc 版本 gcc --version2.安装其他版本的gcc sudo apt install gcc-10 g-10这样我们电脑里包含gcc-9 和 gcc-10两个…

干货分享|HOOPS Web平台和Polygonica进行增材制造的云CAM服务示例

这篇文章提供了一个示例项目&#xff0c;展示了使用 Machineworks Polygonica 和 HOOPS Web 平台进行增材制造的云 CAM 服务。该项目作为一个示例&#xff0c;说明了如何在服务器端使用 Polygonica 与 HOOPS Communicator 和 Exchange 来开发云服务。 它涵盖了增材制造 CAM 的…

三、DSMP/OLS等夜间灯光数据贫困地区识别——MPI和灯光指数拟合、误差分析

一、前言 当我们准备好MPI和灯光指数(包括总灯光指数和平均灯光指数)之后,接下来主要的过程就是通过将MPI和灯光指数拟合,构建多维度指数估算模型,这里我解释一下前文中的MPI计算过程,其实利用熵值法确定指标权重,并通过各 指 标 归 一 化 数 值 乘 以 对 应 的 权 重 …

非监督学习

聚类Clustering 查看大量数据点&#xff0c;自动找到彼此相关或相似的数据点 K-means算法 原理 1.随机选择点&#xff0c;找聚类的中心位置。将点分配给簇质心 2.移动簇质心 不断重复这两个步骤 优化目标 成本函数失真函数distortion 在每次迭代中&#xff0c;失真成本…

汽车电子Autosar之以太网SOME/IP(续)

前言 首先&#xff0c;请问大家几个小小问题&#xff0c;你清楚&#xff1a; 你知道什么是SOME/IP SD吗&#xff1f;SOME/IP-SD有何作用呢&#xff1f;SOME/IP-SD 包含哪些内容呢&#xff1f;SOME/IP-TP 为什么会存在&#xff1f; 今天&#xff0c;我们就来一起探索并回答这…

STM32开发——非标协议(DH11+LCD1602)

1.STM32分文件实现代码 编译的总文件夹dh11andlcd&#xff0c;C文件不能跨文件夹查找&#xff0c;新增的分文件&#xff0c;需要都放调用的文件夹下 C文件和H文件理解&#xff1a;H文件是门脸&#xff0c;放在前面给别人的&#xff0c;别人一看就知道有什么东西。C是给内部人用…

总结899

目标规划&#xff1a; 月目标&#xff1a;6月&#xff08;线性代数强化9讲&#xff0c;背诵15篇短文&#xff0c;考研核心词过三遍&#xff09; 周目标&#xff1a;线性代数强化3讲&#xff0c;英语背3篇文章并回诵&#xff0c;检测 今日已做&#xff1a; 1.读了两篇文章&a…

python使用pyinstaller打包运行过程中莫名的被阻塞

问题描述 使用pyinstaller打包python代码命令 python -m PyInstaller -i logo.ico -F -p ./console -n scl_runner ./main.py运行之后会有一个终端&#xff0c;可以看到终端日志输出正常&#xff0c;多次远程调用也没有问题&#xff0c;死循环测试调用10万次也没有卡死 然…

【Flume】高级组件之Sink Processors及项目实践(Sink负载均衡和故障转移)

文章目录 1. 组件简介2. 项目实践2.1 负载均衡2.1.1 需求2.1.2 配置2.1.3 运行 2.2 故障转移2.2.1 需求2.2.2 配置2.2.3 运行 1. 组件简介 Sink Processors类型包括这三种&#xff1a;Default Sink Processor、Load balancing Sink Processor和Failover Sink Processor。 Defa…

kotlin学习(二)泛型、函数、lambda、扩展、运算符重载

文章目录 泛型&#xff1a;in、out、where型变&#xff08;variance&#xff09;不变&#xff08;Invariant&#xff09;协变&#xff08;Covariant&#xff09;Java上界通配符<? extends T>Kotlin的关键词 outUnsafeVariance 逆变&#xff08;Contravariant&#xff09…

GBASE金融信创优秀解决方案鉴赏 · 核心业务系统数据库解决方案

为此&#xff0c;实验室特别开设金融信创优秀解决方案专栏&#xff0c;集中展示优秀成果。现在&#xff0c;让我们一起来领略下GBASE的优秀解决方案吧~可点击阅读原文 →《金融信创优秀解决方案--核心业务系统数据库解决方案》。 核心业务系统数据库解决方案 方案简介 随着技…

C++:虚函数

C面向对象的三个特性&#xff0c;封装继承多态。在继承的关系中&#xff0c;所有的东西都可以被继承下来&#xff0c;如数据可以被继承下来在内存&#xff0c;而函数的继承则是继承调用权。 虚函数主要是通过虚函数表来实现&#xff0c;每个类都有自己的虚表&#xff0c;当你创…

A fight among three “三国”混战 | 经济学人20230520版社论双语精翻

《经济学人》2023年5月20日封面&#xff08;社论&#xff09;文章精翻&#xff1a;《全球支付系统的“三国”混战》&#xff08;A fight among three&#xff09; A fight among three “三国”混战 The fight over the future of global payments 全球支付的未来之争 Digital …

【STM32】软件I2C(支持多字节)

I2C简介 I2C总线是一种串行、半双工的总线&#xff0c;主要用于近距离、低速的芯片之间的通信。I2C总线有两根双向的信号线&#xff0c;一根数据线SDA用于收发数据&#xff0c;一根时钟线SCL用于通信双方时钟的同步。 在一个i2c通讯总线中&#xff0c;可连接多个i2c通讯设备&a…

Go-unsafe详解

Go语言unsafe包 Go语言的unsafe包提供了一些底层操作的函数&#xff0c;这些函数可以绕过Go语言的类型系统&#xff0c;直接操作内存。虽然这些函数很强大&#xff0c;但是使用不当可能会导致程序崩溃或者产生不可预料的行为。因此&#xff0c;使用unsafe包时必须小心谨慎。 …

吴恩达ChatGPT《Prompt Engineering》笔记

ChatGPT 提示词工程师教程 1. 课程介绍 1.1 ChatGPT 相关术语 LLM&#xff1a;Large Language Model&#xff0c;大语言模型 Instruction Tuned LLM&#xff1a;经过指令微调的大语言模型 Prompt&#xff1a;提示词 RLHF&#xff1a;Reinforcement Learning from Human F…

机器视觉初步6:图像分割专题

图像分割是一种图像处理技术&#xff0c;它将图像划分为具有相似特征的区域。常见的图像分割方法包括阈值分割、边缘分割、区域分割、基于阈值的方法、基于边缘的方法、基于区域的方法、聚类分割、基于图论的方法、基于深度学习的方法。 文章目录 1.阈值分割2.边缘分割3.区域分…