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

news2024/11/18 21:47:46

文章目录

  • 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中,作为策略的容器。使用时传入标识符就可以获取到对应的策略执行了。

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

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

相关文章

<C++> C++11 新的类功能

C11 新的类功能 1.默认成员函数 原来C类中&#xff0c;有6个默认成员函数&#xff1a; 构造函数析构函数拷贝构造函数拷贝赋值重载取地址重载const取地址重载 最后重要的是前4个&#xff0c;后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的。 C11 新增了两…

大家知道什么是CDN吗?对网站有什么帮助?

&#x1f482; 个人网站:【海拥】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 目录 前言什么是CDN&#xf…

【Python 随练】自由落体运动

题目&#xff1a; 一球从 100 米高度自由落下&#xff0c;每次落地后反跳回原高度的一半&#xff1b;再落下&#xff0c;求它在第 10 次落地时&#xff0c;共经过多少米&#xff1f;第 10 次反弹多高&#xff1f; 简介&#xff1a; 在本篇博客中&#xff0c;我们将解决一个物…

华为OD机试之阿里巴巴找黄金宝箱(IV)(Java源码)

阿里巴巴找黄金宝箱(IV) 题目描述 一贫如洗的樵夫阿里巴巴在去砍柴的路上&#xff0c;无意中发现了强盗集团的藏宝地&#xff0c;藏宝地有编号从0-N的箱子&#xff0c;每个箱子上面有一个数字&#xff0c;箱子排列成一个环&#xff0c;编号最大的箱子的下一个是编号为0的箱子。…

[进阶]网络通信:概述、IP地址、InetAddress

什么是网络编程&#xff1f; 可以让设备中的程序与网络上其他设备中的程序进行数据交互&#xff08;实现网络通信的&#xff09;。 Java提供的网络编程解决方案就是在java.net.*包下进行网络编程。 基本的通信架构 基本的通信架构有2种形式&#xff1a;CS架构&#xff08;Cl…

C++IO流和类型处理(11)

IO流 IO流包括 标准IO流&#xff0c;字符串流&#xff0c;文件流 标准IO流 基础使用 #include <iostream> //包括istream和ostream cin >> ----- 标准输入 cout<< ----- 标准输出 clog<< ----- 带缓冲区的标准错误 cerr<< ----- 不带缓冲…

lazada、速卖通、煤炉、eBay 、亚马逊测评环境系统:如何掌握核心养号技巧?

作为一个准备跨足测评行业的业者&#xff0c;或是一个正在考虑将电商业务转向测评服务的卖家&#xff0c;一份详尽的养号指南绝对是你不可错过的知识宝库。 跨境电商平台无疑是巨大的数据中心&#xff0c;它们不仅检测你的设备参数和IP&#xff0c;还分析你的购物习惯&#xf…

mapbox-gl 点位编辑功能

文章目录 前言方式一&#xff1a;借助 Marker添加自定义icon添加POI图层&#xff0c;绑定对应事件基于Marker交互创建自定义Marker编辑 / 创建POI 方式二&#xff1a;采用 mapbox-gl-draw 插件总结 前言 矢量在线编辑是gis常用的编辑功能&#xff0c;兴趣点&#xff08;POI&am…

力扣算法练习(一)

目录 1. 两数相加&#xff08;2&#xff09; 2. 寻找两个正序数组的中位数&#xff08;4&#xff09; 1. 两数相加&#xff08;2&#xff09; 给你两个 非空 的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的&#xff0c;并且每个节点只能存储…

MySQL 中的常用函数详解

0️⃣前言 MySQL是一种常用的关系型数据库管理系统&#xff0c;它提供了许多内置函数来处理数据。本文将介绍MySQL中的各种常用函数&#xff0c;包括字符串函数、日期函数、数学函数、聚合函数等。 文章目录 0️⃣前言1️⃣字符串函数1.1CONCAT函数1.2SUBSTRING函数1.3REPLACE函…

高级SQL语句1

高级SQL语句 建立实验环境高级语句1.---- SELECT ----显示表格中一个或数个字段的所有数据记录2.---- DISTINCT ----不显示重复的数据记录3.---- WHERE ----有条件查询4.---- AND OR ----且 或 &#xff08;一般配合where使用&#xff09;5.---- IN ----显示已知的值的数据记录…

私家云二代/比特米盒安装Armbian Blueseye到Emmc

快捷导航 私家云二代/比特米盒安装Armbian Blueseye到Emmc设备介绍前期准备硬件清单Windows电脑一台Type-C数据线一根U盘或SD卡一个键盘一把显示器HDMI数据线 软件清单Amlogic USB Burning ToolUSB烧录工具SSH工具 下载清单Amlogic刷机工具USB烧录工具ATV6.imgDTB文件BIN文件切…

Spring Security OAuth2授权原理、流程与源码解读

文章目录 前言AuthorizationServerConfigurerAdapter(身份认证服务配置适配器)OAuth2AuthorizationServerConfiguration(OAuth2授权服务配置) EnableAuthorizationServer(开启身份认证服务)AuthorizationServerEndpointsConfigurations身份认证服务站点配置类AuthorizationEndp…

HITSZ嵌入式计算(研)23年Keil模拟器项目解决方案

HITSZ嵌入式计算&#xff08;研&#xff09;23年Keil模拟器项目解决方案 1. 项目介绍2. Keil安装3. 创建新项目3.1 参考博文3.2 流程 4. 发送串口数据4.1 参考博文4.2 串口收发流程 5. 产生波形5.1 头文件封装5.2 初始化GPIO口5.3 产生并观察方波 6. Keil信号函数和中断6.1 中断…

佩戴舒适的蓝牙耳机有哪些品牌?不伤耳的蓝牙耳机推荐

​真无线蓝牙耳机逐渐成为大家日常必不可少的数码产品&#xff0c;也随着耳机的发展&#xff0c;人们对蓝牙耳机的要求也越来越高&#xff0c;不仅音质要好&#xff0c;长时间佩戴也要舒适&#xff0c;更是能够应用于多种场景中使用&#xff0c;但挑选蓝牙耳机也是一门学问&…

kettle开发-Day38-超好用自定义数据处理组件

目录 前言&#xff1a; 一、半斤八两&#xff0c;都不太行 1、表输入&#xff0c;速度快&#xff0c;但不稳妥 2、稳的一批&#xff0c;但是慢的像蜗牛 二、各诉衷肠&#xff0c;合作共赢 1、表输入&#xff0c;高效数据插入 2、插入更新&#xff0c;一个都不能少 三、表输…

## flink- mysql同步数据至starrocks-2.5.0之数据同步

flink- mysql同步数据至starrocks-2.5.0之数据同步 mysql 创建 表 CREATE TABLE t_user (id bigint NOT NULL AUTO_INCREMENT,user_name varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,age int DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB…

手机app测试杂谈

手机上的 app 分为基于 HTML5 的 app(类似于 pc 上的 b/S 应用)和本地 app(类似于 C/S 结构)。 所以测试上我们也可以充分吸收 web 的 b/s 和 c/s 测试经验。但是不同于 pc 上的应用 测试&#xff0c;手机上的测试有其独特性 测试前的思考:我们这个产品主要是做什么的?为什么我…

03. 青龙面板配置B站快速升级任务天选时刻脚本(保姆级图文)

目录 功能介绍与环境要求1. 修改配置文件拉取.sh脚本2. 拉取库脚本3. 安装 dotnet 环境4.1 扫码登录方式4.2 b站cookie方式登录&#xff08;如果你扫码成功了就不用看这个了&#xff09;获取cookie新建cookie的环境变量 5. 配置任务设置变量6. 运行每日任务测试一下总结 欢迎关…

搭建cloud项目以及各个依赖和配置说明

文章目录 背景步骤配置父pom文件spring-cloud和spring-cloud-alibaba的区别 添加网关模块配置网关的application.yml文件网关入口 普通模块普通模块的配置文件&#xff1a;普通模块的pom文件启动类&#xff1a;application.yml文件和bootstrap.yml文件的区别 总结 背景 最近在…