提升代码质量:利用策略模式优化Spring Boot应用的设计

📣前言


   在Spring Boot中使用策略模式(Strategy Pattern)是一种常见的设计模式实践,它允许在运行时选择算法的行为。策略模式定义了一系列的算法,并将每个算法封装起来,使它们可以互换。这样做的好处是使算法的变化独立于使用算法的客户;说白了策略模式是一种行为设计模式,它允许在运行时选择算法的行为。通过定义一系列可互换的算法,策略模式让算法的变化独立于使用算法的客户。
  那么,具体如何实现呢?这将又会是干货满满的一期,全程无尿点不废话只抓重点教,具有非常好的学习效果,拿好小板凳准备就坐!希望学习的过程中大家认真听好好学,学习的途中有任何不清楚或疑问的地方皆可评论区留言或私信,bug菌将第一时间给予解惑,那么废话不多说,直接开整!Fighting!!

🌊环境说明

开发工具:IDEA 2021.3
JDK版本: JDK 1.8
Spring Boot版本:2.3.1 RELEASE
Maven版本:3.8.2


🏆本文收录于《Spring Boot从入门到精通》,专门攻坚指数提升,2024 年国内最系统+最强(更新中)。

本专栏致力打造最硬核 Spring Boot 从零基础到进阶系列学习内容,🚀均为全网独家首发,打造精品专栏,专栏持续更新中…欢迎大家订阅持续学习。 如果想快速定位学习,可以看这篇【SpringBoot教程导航帖】,你想学习的都被收集在内,快速投入学习!!两不误。


正文

以下是在Spring Boot中实现策略模式的基本步骤:

定义策略接口

  1. 定义策略接口

   创建一个策略接口,它定义了所有策略必须实现的方法,例如定义了一个方法 execute(),它是一个没有参数、返回类型为 void 的方法。

/**
 * @Author bug菌
 * @Source 公众号:猿圈奇妙屋
 * @Date 2024-04-08 16:32
 */
public interface Strategy {
    public void execute();
}

   总结来说,Strategy 接口是策略模式的一个简单而强大的实现,它允许算法的可选性和可互换性,从而提高了代码的灵活性和可维护性。

实现具体策略

  1. 实现具体策略

   为每个策略创建一个类,实现策略接口。这里我们就定义仨个,分别为策略A、策略B和其他策略。策略接口定义如下,仅供参:

StrategyA.java

/**
 * @Author bug菌
 * @Source 公众号:猿圈奇妙屋
 * @Date 2024-04-08 16:32
 */
public class StrategyA implements Strategy {

    @Override
    public void execute() {
        // 实现策略A的具体行为
        System.out.println("执行策略A!");
    }
}

StrategyB.java

/**
 * @Author bug菌
 * @Source 公众号:猿圈奇妙屋
 * @Date 2024-04-08 16:32
 */
public class StrategyB implements Strategy {

    @Override
    public void execute() {
        // 实现策略B的具体行为
        System.out.println("执行策略B!");
    }
}

StrategyOther.java

/**
 * @Author bug菌
 * @Source 公众号:猿圈奇妙屋
 * @Date 2024-04-08 16:32
 */
public class StrategyOther implements Strategy {

    @Override
    public void execute() {
        // 实现策略other的具体行为
        System.out.println("执行策略other!");
    }
}

  以上三个策略接口就定义好了,其中我没有具体些策略执行逻辑,只是通过控制台打印信息模拟策略执行了对应策略逻辑,若是实际开发中,大家就按实际的业务逻辑进行代码实现即可。

  总的来说,策略模式允许定义一系列算法,把它们一个个封装起来,并使它们可以相互替换。在这些例子中,StrategyA类提供了一种算法的实现,即打印一条消息。如果需要另一种行为,可以创建另一个实现了Strategy接口的类(比如StrategyB、StrategyOther等),并在该类中提供不同的execute方法实现。然后,可以在运行时动态地选择使用哪个策略。

创建上下文类

  1. 创建上下文类

   由于客户端代码通常会有一个上下文(Context),它包含一个 Strategy 接口类型的引用。在运行时,根据需要选择并设置具体的策略实现。客户端代码可以通过调用 execute 方法来执行策略,而无需关心具体的策略实现细节。为此,我们创建一个上下文类,它包含一个策略对象,并允许在运行时更改策略。实现代码如下:仅供参考

/**
 * @Author bug菌
 * @Source 公众号:猿圈奇妙屋
 * @Date 2024-04-08 16:32
 */
public class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public void executeStrategy() {
        strategy.execute();
    }
}

   总结来说,Context 类提供了一个执行策略的接口,它允许客户端代码在运行时选择和更改策略,而不需要修改使用这些策略的上下文代码。这种模式提高了代码的灵活性和可维护性,因为它将策略的选择和实现从上下文逻辑中解耦出来。

配置Spring Boot

  1. 配置Spring Boot

   在Spring Boot应用中,你可以使用@Configuration@Bean注解来配置策略实现,并将其注册为Spring容器中的Bean。其中,使用@Configuration注解,表明它是一个Spring配置类,用于定义应用程序的bean配置。在这个配置类中,定义了三个bean,分别是strategyA 、strategyB 和 strategyOther。这三个bean分别对应于 Strategy 接口的三个不同实现:StrategyA 、StrategyB 和 StrategyOther。实现代码如下:仅供参考

/**
 * @Author bug菌
 * @Source 公众号:猿圈奇妙屋
 * @Date 2024-04-08 16:33
 */
@Configuration
public class StrategyConfig {
    @Bean
    public Strategy strategyA() {
        return new StrategyA();
    }

    @Bean
    public Strategy strategyB() {
        return new StrategyB();
    }

    @Bean
    public Strategy strategyOther() {
        return new StrategyOther();
    }
}

   其中,@Bean注解用于定义一个bean。当Spring容器启动时,它会扫描所有带有@Configuration注解的类,并调用这些类中所有带有@Bean注解的方法。这些方法的返回值将成为Spring容器中的bean。方法的名称通常作为bean的默认名称。
   因此,strategyA()方法创建的bean将默认被命名为strategyA,strategyB()方法创建的bean将默认被命名为strategyB,以此类推。对于@Bean注解的方法可以有参数,也可以没有参数,可有可无。在这个例子中,strategyA()、strategyB()和strategyOther()方法都没有参数,这意味着它们每次被调用时都会创建一个新的bean实例。

使用策略

  1. 使用策略

   在你的应用中,你可以根据需要注入并使用不同的策略,说白了就是为了根据传入的策略名称执行相应的策略。实现代码如下:仅供参考

/**
 * @Author bug菌
 * @Source 公众号:猿圈奇妙屋
 * @Date 2024-04-08 16:33
 */
@Service
public class StrategyService {
    @Autowired
    private ApplicationContext applicationContext;

    public void executeStrategy(String strategyName) {
        Strategy strategy = null;
        if ("A".equals(strategyName)) {
            strategy = applicationContext.getBean("strategyA", Strategy.class);
        } else if ("B".equals(strategyName)) {
            strategy = applicationContext.getBean("strategyB", Strategy.class);
        }else{
            strategy = applicationContext.getBean("strategyOther", Strategy.class);
        }

        if (strategy != null) {
            Context context = new Context(strategy);
            context.executeStrategy();
        }
    }
}

   在上段代码中,@Service 注解表明这个类是一个Spring服务组件,Spring容器会创建这个类的实例并管理其生命周期。这样,其他组件就可以通过依赖注入(DI)来使用 StrategyService 提供的功能。而@Autowired 注解用于自动注入Spring容器中的 ApplicationContext 实例。其中,ApplicationContext 是Spring的核心接口,提供了许多方法来管理bean,如获取bean实例、发布事件等。 而对于Context类,在构造函数中接收策略实例,然后 executeStrategy() 方法会调用这个策略实例的 execute()方法来执行具体的算法。

   对于executeStrategy方法,我做如下几点解释:

  • executeStrategy() 方法接受一个策略名称 strategyName 作为参数,并根据这个名称来选择并执行相应的策略。
  • 方法内部首先定义了一个 Strategy 类型的变量 strategy,它将用来存储获取到的策略实例。
  • 接着,我们使用 if-else 判断语句根据传入的 strategyName 来获取对应的策略bean。这里假设 strategyA 和 strategyB 是在配置类 StrategyConfig 中定义的bean名称。
  • 如果 strategyName 是 “A” 或 “B”,则通过 applicationContext.getBean 方法获取对应的策略实例。如果名称不匹配,假设有一个默认的策略 “strategyOther” 被获取。
  • 如果成功获取到策略实例(strategy != null),则创建一个 Context 类的实例,并将策略实例传入。然后调用 Context 类的 executeStrategy() 方法来执行策略。

   总结来说,StrategyService 类通过Spring的依赖注入和应用上下文来动态选择和执行不同的策略,这是策略模式在Spring框架中的一个应用示例。

测试策略

  1. 测试策略

   接着我们定义一个测试代码或者控制器中,调用服务方法并传入不同的策略名称来测试策略模式。这里我们就使用Spring MVC的@RestController注解来定义一个RESTful控制器。这个控制器提供了一个HTTP GET接口,用于执行策略模式中的不同策略。实现代码如下:仅供参考

/**
 * @Author bug菌
 * @Source 公众号:猿圈奇妙屋
 * @Date 2024-04-08 16:34
 */
@RestController
public class StrategyController {

    @Autowired
    private StrategyService strategyService;

    @GetMapping("/execute/{strategyName}")
    public String executeStrategy(@PathVariable String strategyName) {
        strategyService.executeStrategy(strategyName);
        return "Strategy executed: " + strategyName;
    }
}

  对应@RestController注解,它是一个特殊的控制器注解,它是@Controller@ResponseBody的组合。它表明这个类是一个控制器,其方法返回值会自动序列化为JSON或其他格式的响应体,而不是视图名或模板路径。这意味着客户端将直接收到数据,而不是HTML页面。对于executeStrategy()方法:它接受一个名为strategyName的参数,该参数通过URL传递。方法内部调用strategyServiceexecuteStrategy()方法,并传入策略名称。方法返回一个字符串,格式为"Strategy executed: "加上策略名称。这个字符串将被自动序列化为JSON或其他格式的响应体,并发送给客户端。最后该接口请求返回值的是一个描述性字符串,它告诉客户端策略执行的结果。在实际应用中,你可能会返回更复杂的对象或数据结构,而不是简单的字符串。

  通过上述步骤,你可以在Spring Boot应用中灵活地使用策略模式,根据用户请求或其他条件动态地改变算法行为。这种模式提高了代码的可扩展性和可维护性,并且可以根据需求轻松添加新的策略实现。

接口测试

  1. 接口测试
      接着,我们就根据如上定义的接口来进行一波本地测试,检验下是否都达到了预期目的,能够根据所传入的参数执行对应的策略;测试记录如下,仅供参考:

  执行测试前,我们先重启项目,让我们刚写的代码都生效。然后检查下指定的环境及启动端口是多少:

在这里插入图片描述

启动项目成功的表现:

在这里插入图片描述

1、参数传入"A"

  接着,我们通过接口传参的形式,先传入个"A",演示如下:http://localhost:8080/execute/A

  返回到项目中,查验下控制台,是否是执行了对应A策略的逻辑?

在这里插入图片描述

  上述结果很明显是执行成功了!即根据用户请求或其他条件动态地改变算法行为,动态的执行对应的策略逻辑。

2、参数传入"B"

  与上述测试流程一直,这里我们就直接演示及展示结果。传入个"B",演示如下:http://localhost:8080/execute/B


测试结果展示如下:

在这里插入图片描述

3、参数传入"C"

  与上述测试流程一直,这里我们就直接演示及展示结果。传入个"C",而实际中,我们并没有定义策略C,预期结果就是执行默认策略Other。演示如下:http://localhost:8080/execute/C

测试结果展示如下:

在这里插入图片描述

  综上所述三次测试实验,完美的验证了一个目的,就是可动态的执行策略,只需要根据用户请求或其他条件,即可动态地改变算法行为。这种提升代码质量的设计思路,你们学会了吗??

… …

  ok,以上就是我这期的全部内容啦,如果还想学习更多,你可以看看如下的往期热文推荐哦,每天积累一个奇淫小知识,日积月累下去,你一定能成为令人敬仰的大佬。

「赠人玫瑰,手留余香」,咱们下期拜拜~~

🌊热文推荐

滴~如下推荐【Spring Boot 进阶篇】的学习大纲,请小伙伴们注意查收。

Spring Boot进阶(01):Spring Boot 集成 Redis,实现缓存自由

Spring Boot进阶(02):使用Validation进行参数校验

Spring Boot进阶(03):如何使用MyBatis-Plus实现字段的自动填充

Spring Boot进阶(04):如何使用MyBatis-Plus快速实现自定义sql分页

Spring Boot进阶(05):Spring Boot 整合RabbitMq,实现消息队列服务

Spring Boot进阶(06):Windows10系统搭建 RabbitMq Server 服务端

Spring Boot进阶(07):集成EasyPoi,实现Excel/Word的导入导出

Spring Boot进阶(08):集成EasyPoi,实现Excel/Word携带图片导出

Spring Boot进阶(09):集成EasyPoi,实现Excel文件多sheet导入导出

Spring Boot进阶(10):集成EasyPoi,实现Excel模板导出成PDF文件

Spring Boot进阶(11):Spring Boot 如何实现纯文本转成.csv格式文件?

Spring Boot进阶(12):Spring Boot 如何获取Excel sheet页的数量?

Spring Boot进阶(13):Spring Boot 如何获取@ApiModelProperty(value = “序列号“, name = “uuid“)中的value值name值?

Spring Boot进阶(14):Spring Boot 如何手动连接库并获取指定表结构?一文教会你

Spring Boot进阶(15):根据数据库连接信息指定分页查询表结构信息

Spring Boot进阶(16):Spring Boot 如何通过Redis实现手机号验证码功能?

Spring Boot进阶(17):Spring Boot如何在swagger2中配置header请求头等参数信息

Spring Boot进阶(18):SpringBoot如何使用@Scheduled创建定时任务?

Spring Boot进阶(19):Spring Boot 整合ElasticSearch

Spring Boot进阶(20):配置Jetty容器

Spring Boot进阶(21):配置Undertow容器

Spring Boot进阶(22):Tomcat与Undertow容器性能对比分析

Spring Boot进阶(23):实现文件上传

Spring Boot进阶(24):如何快速实现多文件上传?

Spring Boot进阶(25):文件上传的单元测试怎么写?

Spring Boot进阶(26):Mybatis 中 resultType、resultMap详解及实战教学

Spring Boot进阶(27):Spring Boot 整合 kafka(环境搭建+演示)

Spring Boot进阶(28):Jar包Linux后台启动部署及滚动日志查看,日志输出至实体文件保存

Spring Boot进阶(29):如何正确使用@PathVariable,@RequestParam、@RequestBody等注解?不会我教你,结合Postman演示

Spring Boot进阶(30):@RestController和@Controller 注解使用区别,实战演示

… …

  若想系统完整的从0到1的学习,可以参考这篇专栏总结《2024最新首发,全网最全 Spring Boot 学习宝典(附思维导图)》,本专栏致力打造最硬核 Spring Boot 进阶系列学习内容,🚀均为全网独家首发,打造精品专栏,专栏持续更新中。欢迎大家订阅持续学习。

  如果想快速定位学习,可以看这篇【教程导航帖】导航目录,你想学习的都被收集在内,快速投入学习!!两不误。

  在入门及进阶之途,我必助你一臂之力,系统性学习,从入门到精通,带你不走弯路,直奔终点;投资自己,永远性价比最高,都这么说了,你还不赶紧来学??

  本文涉及所有源代码,均已上传至GitHub开源,供同学们一对一参考 GitHub传送门,同时,原创开源不易,欢迎给个star🌟,想体验下被🌟的感jio,非常感谢❗

📣文末

我是bug菌,CSDN | 掘金 | InfoQ | 51CTO | 华为云 | 阿里云 | 腾讯云 等社区博客专家,C站博客之星Top30,华为云2023年度十佳博主,掘金多年度人气作者Top40,掘金等各大社区平台签约作者,51CTO年度博主Top12,掘金/InfoQ/51CTO等社区优质创作者;全网粉丝合计 30w+;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试真题、4000G PDF电子书籍、简历模板等海量资料,你想要的我都有,关键是你不来拿哇。

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

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

相关文章

MyBatis源码中的设计模式1

1. 建造者模式的应用 建造者模式属于创建类模式,通过一步一步地创建一个复杂的对象,能够将部件与其组装过程分开。用户只需指定复杂对象的类型,就可以得到该对象,而不需要了解其内部的具体构造细节。《Effective Java》中也提到&…

CH552G使用IAP下载

常见下载中的方式ISP,IAP,ICP 参考,CH552G中文手册,参考1 ISP:In System Programing,在系统编程。是常见的,使用软件,先将某个引脚(例如boot)连接到合适的电…

领航Linux UDP:构建高效网络新纪元

欢迎来到 破晓的历程的 博客 ⛺️不负时光,不负己✈️ 文章目录 引言Udp和Tcp的异同相同点不同点总结 1.1、socket1.2、bind1.3、recvfrom1.4、sendto2.1、代码2.1、说明3.1、代码3.2、说明 引言 在前几篇博客中,我们学习了Linux网络编程中的一些概念。…

【Django】网上蛋糕项目商城-购物车和我的订单功能

1.购物车功能 在首页中的滚动栏的商品,热门商品,新品,以及商品详情中都有加入购物车按钮 在models文件中创建购物车表,用于保存当前用户添加的商品信息 # 购物车表 class ShoppingCar(models.Model):# 用户iduserIdmodels.Integ…

JRT打印设计器解耦

为了让打印设计器可以给多个产品打印通用,那么设计器就不能嵌入太多具体产品的业务信息。比如医院主键、工作组、医嘱关联登。 设计器在设计表的时候就没引入检验部分的依赖,采用产品组唯一标识和产品组业务ID来隔离不同组的模板设计。 维护菜单时候就…

CTFshow--web--xss

目录 web316 web317~319 web320~326 web327 web328 web329 web330 web331 web332 web333 先在自己的服务器写上代码 <?php$content $_GET[1]; if(isset($content)){file_put_contents(flag.txt,$content); }else{echo no data input; }要拿到管理员的cookie , 而…

Java - 程序员面试笔记记录 实现 - Part5

7.1 Struts 优点&#xff1a; 1. MVC模式实现了表现与逻辑的分离&#xff0c;扩展性高。 2. 提供页面导航功能&#xff0c;通过配置文件建立整个系统各部分之间的联系。 3. 集成了一些常用处理功能。 缺点&#xff1a; 1. 仅面向 Web 应用程序开发 2. Action 非线程安全…

HTML+CSS+JS井字棋(来自动下棋)

井字棋 自动下棋 玩家先下&#xff0c;计算机后下 源码在图片后面 点赞❤️收藏⭐️关注&#x1f60d; 效果图 源代码 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <title>Tic Tac Toe Game</tit…

开始Linux之路

人生得一知己足矣&#xff0c;斯世当以同怀视之。——鲁迅 Linux操作系统简单操作指令 1、ls指令2、pwd命令3、cd指令4、mkdir指令(重要)5、whoami命令6、创建一个普通用户7、重新认识指令8、which指令9、alias命令10、touch指令11、rmdir指令 及 rm指令(重要)12、man指令(重要…

技术周总结 2024.07.08~07.14(算法,Python,Java,Scala,PHP)

文章目录 一、07.13 周六1.0&#xff09;算法题&#xff1a;字符串中的单词反转1.1&#xff09; 问题01:可靠性计算中的MTTR MTTF MTBF 分别指什么&#xff1f;他们之间有什么联系&#xff1f;MTTR (Mean Time to Repair)MTTF (Mean Time to Failure)MTBF (Mean Time Between F…

韦东山嵌入式linux系列-驱动进化之路:总线设备驱动模型-课后作业

在内核源码中搜索 platform_device_register 可以得到很多驱动&#xff0c;选择 一个作为例子&#xff1a; ① 确定它的名字 ② 根据它的名字找到对应的 platform_driver ③ 进入 platform_device_register/platform_driver_register 内部&#xff0c;分析 dev 和 drv 的匹配过…

某林操作系统——网络系统

什么是零拷贝&#xff1f; DMA技术&#xff1a;使用一个DMA控制器将数据从硬盘传输到内存&#xff0c;除了一开始调用DMA控制器发起传输&#xff0c;数据搬运全称不需要CPU参与。 发送一段数据到网上如下所示 4次状态切换4次拷贝。 为了加快速度就要减少上下文切换&#xff…

网络安全防御【防火墙安全策略用户认证综合实验】

目录 一、实验拓扑图 二、实验要求 三、实验思路 四、实验步骤 1、打开ensp防火墙的web服务&#xff08;带内管理的工作模式&#xff09; 2、在FW1的web网页中网络相关配置 3、交换机LSW6&#xff08;总公司&#xff09;的相关配置&#xff1a; 4、路由器相关接口配置&a…

【深度学习入门篇 ⑦】PyTorch池化层

【&#x1f34a;易编橙&#xff1a;一个帮助编程小伙伴少走弯路的终身成长社群&#x1f34a;】 大家好&#xff0c;我是小森( &#xfe61;ˆoˆ&#xfe61; ) &#xff01; 易编橙终身成长社群创始团队嘉宾&#xff0c;橙似锦计划领衔成员、阿里云专家博主、腾讯云内容共创官…

鸿蒙开发:Universal Keystore Kit(密钥管理服务)【查询密钥是否存在(ArkTS)】

查询密钥是否存在(ArkTS) HUKS提供了接口供应用查询指定密钥是否存在。 开发步骤 指定密钥别名keyAlias&#xff0c;密钥别名最大长度为64字节。初始化密钥属性集。用于查询时指定密钥的属性TAG&#xff0c;比如查询的密钥范围(全量/单个)&#xff0c;当查询单个时&#xff…

工时计算软件比较:哪些功能是必需的?

国内外主流的10款工时计算软件对比&#xff1a;PingCode、Worktile、蓝凌OA、用友软件、金蝶软件、智能人事、Time Doctor、Toggl Track、Clockify、Harvest。 在选择合适的工时计算软件时&#xff0c;很多企业面临的一个主要痛点是如何找到既能满足功能需求又具有成本效益的解…

虚拟机及其Debian(kali)安装

本机电脑为Windows10系统专业版&#xff0c;在此基础上安装VMware和系统&#xff08;Kali&#xff09; 步骤如下 一、安装 VMware Workstation Pro v16.2.4 安装步骤可参照网上博客&#xff0c;该步骤较简单&#xff0c;此处不做讲解。文件中共计两个&#xff0c;其中一个是激活…

YOLOv8白皮书-第Y8周:yolov8.yaml文件解读

本文为365天深度学习训练营中的学习记录博客 原作者&#xff1a;K同学啊|接辅导、项目定制 请根据YOLOv8n、YOLOv8s模型的结构输出&#xff0c;手写出YOLOv8l的模型输出 文件位置&#xff1a;./ultralytics/cfg/models/v8/yolov8.yaml 一、参数配置 # Parameters nc: 80 # n…

android APP在蓝牙模拟键盘扫描条码设备开机时闪退

&#x1f3c6;本文收录于《CSDN问答解答》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&…

下载安装VSCode并添加插件作为仓颉编程入门编辑器

VSCode下载地址&#xff1a;下载 Visual Studio Code - Mac、Linux、Windows 插件下载&#xff1a;GitCode - 全球开发者的开源社区,开源代码托管平台 仓颉社区中下载解压 cangjie.vsix 插件 打开VSCode 按 Ctrl Shift X 弹出下图 按照上图步骤依次点击选中我们下…