Bootstrap

提升代码质量:利用策略模式优化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电子书籍、简历模板等海量资料,你想要的我都有,关键是你不来拿哇。

;