• Spring framework Day11:策略模式中注入所有实现类


    前言

    什么是策略模式

    策略模式(Strategy Pattern)是一种面向对象设计模式,它定义了算法族(一组相似的算法),并且将每个算法都封装起来,使得它们可以互相替换。策略模式让算法的变化独立于使用算法的客户端。

    在策略模式中,定义一个抽象的策略接口或者抽象类来封装不同的具体算法实现,并由客户端根据需要动态选择使用哪种算法。这种方式支持应用程序灵活地更换算法和扩展算法,而无需修改已有代码。此外,策略模式可以减少大量的 if-else 语句,提高代码的可读性和可维护性。

    策略模式通常包含三个角色:

    1. 环境(Context)角色:通过一个持有某个策略实例的变量来调用具体的策略算法。

    2. 抽象策略(Strategy)角色:定义了一个公共的接口或抽象类,规定了所有具体策略角色必须实现的方法。

    3. 具体策略(Concrete Strategy)角色:实现了抽象策略接口或抽象类中定义的方法,提供具体的处理逻辑。每个具体策略角色都代表一个算法的具体实现。

    在使用策略模式时,首先定义一个抽象的策略接口或抽象类,然后定义具体的策略实现类,最后将策略实现类注入到需要使用的类中。这样可以让客户端通过改变具体的策略实现类,来灵活地选择不同的算法,从而实现目标。

     

    一、开始学习

    本次案例,通过支付的例子来完成一个案例。

    1、新建项目,结构如下

    2、添加 spring 依赖
    1. <dependencies>
    2. <dependency>
    3. <groupId>org.springframeworkgroupId>
    4. <artifactId>spring-contextartifactId>
    5. <version>5.3.23version>
    6. dependency>
    7. <dependency>
    8. <groupId>ch.qos.logbackgroupId>
    9. <artifactId>logback-classicartifactId>
    10. <version>1.4.5version>
    11. dependency>
    12. dependencies>
     3、在 service 包下新建一个 Payment 接口,在 impl 包下新建 AliPayment、WeChartPayment实现类

    Payment 接口

    1. /**
    2. * @Date 2023-10-07
    3. * @Author qiu
    4. * 支付接口,对应有不同的实现
    5. */
    6. public interface Payment {
    7. /**
    8. * 支付方法
    9. * @param money
    10. */
    11. void pay(BigDecimal money);
    12. }

    Alipayment 实现类

    1. /**
    2. * @Date 2023-10-07
    3. * @Author qiu
    4. * 支付宝支付
    5. */
    6. @Service
    7. @Slf4j
    8. public class AliPayment implements Payment {
    9. @Override
    10. public void pay(BigDecimal money) {
    11. log.info("使用支付宝支付金额: " + money.doubleValue());
    12. }
    13. }

    WeChartPayment 实现类

    1. /**
    2. * @Date 2023-10-07
    3. * @Author qiu
    4. * 微信支付
    5. */
    6. @Slf4j
    7. @Service
    8. public class WeChartPayment implements Payment {
    9. @Override
    10. public void pay(BigDecimal money) {
    11. log.info("使用微信支付金额:" + money.doubleValue());
    12. }
    13. }

    看下图分析:

     在策略模式中,定义一个抽象的策略接口或者抽象类来封装不同的具体算法实现,并由客户端根据需要动态选择使用哪种算法。

    现在大家可以理解这句话的意思了吧,我把支付的方法抽象出来,定义一个接口,但是具体的实现交给了实现它的实现类去完成,每一种实现都是独立的,如果现在需要新增一个支付方式该怎么办呢?很简单,只需要再新增一个实现类去继承 Payment 支付接口即可,具体的实现也是这个实现类去完成,把每个不同类型的支付方式都分别交给独立的实现类去完成,交给用户去选择。这样写代码也更加方便维护,使用微信支付出现问题时我们只需要去修改 WeChartPayment 实现类的代码,不需要改动其他的实现类的代码,而且修改 WeChartPayment 的代码也不会影响到其他实现类的正常运行,这就是策略模式。

    4、如何使用 spring 注入所有的实现类呢?
    1)新增一个 PaymentContext 类
    1. @Service
    2. /**
    3. * 利用 Lombok 生成一个带参数的构造方法
    4. * 这样即可以通过构造方法直接注入
    5. */
    6. @RequiredArgsConstructor
    7. public class PaymentContext {
    8. /**
    9. * 构造方法注入
    10. * 注入一个 map 集合,spring 会将 Payment 接口的所有实现类
    11. * 一并保存到 map 中
    12. * key(bean 的 id) 为支付类型, value 是具体的支付策略实现
    13. */
    14. private final Map paymentMap;
    15. /**
    16. * 根据支付类型选择具体的策略来完成支付
    17. * @param paymentType 支付类型
    18. * @param money 支付金额
    19. */
    20. public void pay(String paymentType, BigDecimal money){
    21. Payment payment = paymentMap.get(paymentType);
    22. payment.pay(money);
    23. }
    24. }

    这是一个策略上下文类 PaymentContext,它使用了构造方法注入来获取支付策略的集合。

    注解 @Service 表明该类是一个服务类,用于处理业务逻辑。

    注解 @RequiredArgsConstructor 是 Lombok 提供的注解,它会生成一个带有 final 字段的构造函数。在这个类中,通过构造方法注入了一个 Map 类型的成员变量 paymentMap。Spring 会将所有实现了 Payment 接口的 bean 注册到这个 paymentMap 中,key 为 bean 的 id,value 为相应的具体支付策略实现。

    pay 方法中,通过传入的 paymentType 参数从 paymentMap 中获取对应的具体支付策略,并调用其 pay 方法来完成支付操作。

    通过这种方式,可以在策略上下文中动态选择具体的支付策略进行支付。通过构造方法注入支付策略的集合,可以方便地扩展和管理不同支付类型的策略。

    2)在 controller 包下新增一个 PaymentContorller 类
    1. @Controller
    2. @RequiredArgsConstructor
    3. public class PaymentController {
    4. /**
    5. * 注入策略上下文
    6. */
    7. private final PaymentContext context;
    8. public void pay(String type, BigDecimal money) {
    9. context.pay(type, money);
    10. }
    11. }

    这是一个支付控制器类 PaymentController,它使用了策略模式来处理不同类型的支付。

    注解 @Controller 表明该类是一个控制器,用于接收和处理请求。

    注解 @RequiredArgsConstructor 是 Lombok 提供的注解,它会生成一个包含所有 final@NonNull 注解的字段的构造函数。

    控制器类中声明了一个名为 contextPaymentContext 类型的成员变量,用于存储策略上下文对象。

    pay 方法中,根据传入的 typemoney 参数,调用 contextpay 方法来执行具体的支付逻辑。这里的 pay 方法是策略上下文对象中定义的方法,用于根据支付类型调用相应的具体支付策略。

    通过这种方式,可以将不同类型的支付逻辑封装到不同的具体支付策略中,并通过策略上下文来选择并执行相应的支付策略。这样可以实现支付方式的灵活切换和扩展。

    5、在 resources 下新建一个 spring 的 xml 文件 beans.xml 
    1. "1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xmlns:context="http://www.springframework.org/schema/context"
    5. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    6. <context:component-scan base-package="edu.nf.ch09"/>
    7. beans>
    6、测试
    1. public class Main {
    2. public static void main(String[] args) {
    3. ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    4. PaymentContext bean = context.getBean(PaymentContext.class);
    5. bean.pay("weChartPayment", new BigDecimal("100"));
    6. }
    7. }

    运行结果

    这是一个启动类 Main,它通过 Spring 容器来获取支付上下文对象,并调用其 pay 方法进行支付操作。

    main 方法中,利用 ClassPathXmlApplicationContext 类加载 classpath 下的 beans.xml 文件,从而创建一个 Spring 容器。

    使用容器的 getBean 方法获取 id 为 paymentContext 的 bean 对象,即上下文对象 PaymentContext

    最后,通过调用上下文对象的 pay 方法来完成支付操作。这里传入的第一个参数是支付类型,在 beans.xml 中扫描的 bean id 即为对应的支付类型;第二个参数为支付金额。

    在运行动图中可以看到,我们需要使用哪种支付方式就把它的 bean id写进去即可。

    通过 Spring 容器的支持,我们可以方便地管理和维护各个支付类型的具体支付策略,同时也能够方便地进行扩展和配置。

     

    二、使用策略模式注入所有实现类的好处 

    使用策略模式注入所有实现类的好处主要有以下几个方面:

    1. 解耦性:通过策略模式,将具体的实现类与调用它们的类解耦。调用方只需要依赖于抽象的策略接口或基类,而不需要关心具体的实现类。这样可以降低类之间的耦合度,并且使得系统更加灵活和可维护。

    2. 可扩展性:当新增一种支付类型时,只需实现相应的支付策略,并注册到容器中即可,无需修改调用方的代码。通过容器自动注入所有实现类,实现类的新增和移除变得方便快捷,可以根据业务需求随时扩展支付策略。

    3. 可配置性:通过注入所有实现类,可以将不同的实现类配置到容器的配置文件中,而不需要修改源代码。这样在不同的环境中,可以通过简单的配置文件修改支付策略的选择,而无需重新编译和部署代码。

    4. 单一职责原则:通过策略模式,每个具体的支付策略类只需要关注自身特定的支付逻辑,符合单一职责原则。这样可以提高代码的可读性、可维护性和可测试性。

    总之,使用策略模式注入所有实现类具有解耦性、可扩展性、可配置性和单一职责原则等优点,使得系统更加灵活、可维护和可测试。

    三、gitee 案例

    案例完整地址:https://gitee.com/qiu-feng1/spring-framework.git

  • 相关阅读:
    00-从JAVA工程师的角度快速上手Python-基础语法
    Nacos注册中心
    无需编写任何代码即可使用 AI 创建新漫画,关于如何使用 GPT-3 和 DALL-E 为有趣页面生成原创内容的教程
    makedown文字上色技巧
    Visual Studio 2022插件的安装及使用 - 编程手把手系列文章
    vector底层实现及应用注意事项
    138.【JUC并发编程- 03】
    Backblaze 2023 Q3硬盘故障质量报告解读
    GitHub星标超70K,阿里大佬的架构总结“分布式全解”笔记霸榜
    使用Docker安装JupyterHub
  • 原文地址:https://blog.csdn.net/zhiqiuqiu2/article/details/133787679