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

news2024/11/28 18:58:31

前言

什么是策略模式?

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

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

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

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

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

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

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

 

一、开始学习

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

1、新建项目,结构如下

2、添加 spring 依赖
 
    <!-- spring 的核心依赖 -->
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.23</version>
        </dependency>
 
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.4.5</version>
        </dependency>
 
    </dependencies>
 3、在 service 包下新建一个 Payment 接口,在 impl 包下新建 AliPayment、WeChartPayment实现类

Payment 接口

/**
 * @Date 2023-10-07
 * @Author qiu
 * 支付接口,对应有不同的实现
 */
public interface Payment {

    /**
     * 支付方法
     * @param money
     */
    void pay(BigDecimal money);

}

Alipayment 实现类

/**
 * @Date 2023-10-07
 * @Author qiu
 * 支付宝支付
 */
@Service
@Slf4j
public class AliPayment implements Payment {
    @Override
    public void pay(BigDecimal money) {
        log.info("使用支付宝支付金额: " + money.doubleValue());
    }
}

WeChartPayment 实现类

/**
 * @Date 2023-10-07
 * @Author qiu
 * 微信支付
 */
@Slf4j
@Service
public class WeChartPayment implements Payment {
    @Override
    public void pay(BigDecimal money) {
        log.info("使用微信支付金额:" + money.doubleValue());
    }
}

看下图分析:

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

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

4、如何使用 spring 注入所有的实现类呢?
1)新增一个 PaymentContext 类

@Service
/**
 * 利用 Lombok 生成一个带参数的构造方法
 * 这样即可以通过构造方法直接注入
 */
@RequiredArgsConstructor
public class PaymentContext {

    /**
     * 构造方法注入
     * 注入一个 map 集合,spring 会将 Payment 接口的所有实现类
     * 一并保存到 map 中
     * key(bean 的 id) 为支付类型, value 是具体的支付策略实现
     */
    private final Map<String, Payment> paymentMap;

    /**
     * 根据支付类型选择具体的策略来完成支付
     * @param paymentType 支付类型
     * @param money 支付金额
     */
    public void pay(String paymentType, BigDecimal money){
    Payment payment = paymentMap.get(paymentType);
    payment.pay(money);
    }

}

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

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

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

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

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

2)在 controller 包下新增一个 PaymentContorller 类

@Controller
@RequiredArgsConstructor
public class PaymentController {

    /**
     * 注入策略上下文
     */
    private final PaymentContext context;


    public void pay(String type, BigDecimal money) {
        context.pay(type, money);
    }

}

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

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

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

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

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

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

5、在 resources 下新建一个 spring 的 xml 文件 beans.xml 
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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">
 
    <!-- 启用包扫描 -->
    <context:component-scan base-package="edu.nf.ch09"/>
 
</beans>
6、测试
public class Main {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        PaymentContext bean = context.getBean(PaymentContext.class);
        bean.pay("weChartPayment", new BigDecimal("100"));
    }

}

运行结果

这是一个启动类 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

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

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

相关文章

LeetCode【17】电话号码的字母组合

题目&#xff1a; 思路&#xff1a; 参考&#xff1a;https://blog.csdn.net/weixin_46429290/article/details/121888154 和上一个题《子集》的思路一样&#xff0c;先画出树结构&#xff0c;看树的深度&#xff08;遍历层级&#xff09;&#xff0c;树的宽度&#xff08;横向…

压力山大题

找不到工作的面试者总结 提示&#xff1a;写文章的时候&#xff0c;我还在找工作&#xff01;&#xff01;&#xff01; 文章目录 找不到工作的面试者总结前言一、JAVA面死题1. OOP是什么2. 重载与重写的区别3. java基本类型4. String、StringBuffer、StringBuilder的区别 二、…

2022最新版-李宏毅机器学习深度学习课程-P23 为什么用了验证集结果还是过拟合

用了验证集还有可能会过拟合 这个片段可以从理论上证明这一点 以上整个挑选模型的过程也可以想象为一种训练。 把三个模型导出的最小损失公式看成一个集合&#xff0c;现在要做的就是在这个集合中找到某个h&#xff08;此处可以视为训练&#xff09;&#xff0c;使得在验证集…

边写代码边学习之Pycaret

PyCaret 简介 PyCaret 是一个用于简化 Python 机器学习工作流程的开源库。它提供了一个高级、低代码的接口&#xff0c;用于自动化机器学习流程的各个方面&#xff0c;使数据科学家和分析师更容易构建和部署机器学习模型。PyCaret 的一些关键特点和用途包括&#xff1a; 1. 自…

第六章 查找

第六章 查找 基本概念静态查找表顺序表上的查找有序表上的查找索引顺序表上的查找 二叉排序树散列表常见散列法散列表的实现 小试牛刀 基本概念 查找表是由同一类型的数据元素构成的集合&#xff0c;它是一种以查找为“核心”&#xff0c;同时包括其他运算的非常灵活的数据结构…

Android Studio SDK manager加载packages不全

打开Android Studio里的SDK manager&#xff0c;发现除了已安装的&#xff0c;其他的都不显示。 解决方法&#xff1a; 设置代理&#xff1a; 方便复制> http://mirrors.neusoft.edu.cn/ 重启Android Studio

小主机折腾记17

8月9月10月基本在出差&#xff0c;流水账如下 1.由于出差&#xff0c;租了个公寓&#xff0c;所以买了个r2s&#xff0c;卖家已经安装部署好openwrt&#xff0c;风扇以及无线网卡 着重研究了风扇的脚本以及无线网卡的设置 风扇可以完美设置&#xff0c;但是无线网卡效果差强人意…

k8s-11 网络策略

添加网络策略 限制pod流量 控制的对象是具有appmyapp-v1标签的pod 此时访问svc是不通的 给测试pod添加指定标签后&#xff0c;可以访问 重启一下 限制namespace流量 给namespace添加指定标签 同时限制namespace和pod 给test命令空间中的pod添加指定标签后才能访问 限制集群…

k8s-13 存储之secret

Secret 对象类型用来保存敏感信息&#xff0c;例如密码、OAuth 令牌和 ssh key。 敏感信息放在 secret 中比放在 Pod 的定义或者容器镜像中来说更加安全和灵活 。 Pod 可以用两种方式使用 secret:作为 volume 中的文件被挂载到 pod 中的一个或者多个容器里 当 kubelet 为 pod 拉…

山西电力市场日前价格预测【2023-10-16】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-10-16&#xff09;山西电力市场全天平均日前电价为356.38元/MWh。其中&#xff0c;最高日前电价为502.82元/MWh&#xff0c;预计出现在18: 30。最低日前电价为224.63元/MWh&#xff0c;预计…

PTQ与QAT

对称量化与非对称量化 量化分为对称量化与非对称量化。 非对称量化含有S和Z&#xff0c;对称量化Z为0&#xff0c;计算公式中只需S&#xff0c;为非饱和量化。 动态范围的确认 动态范围的确认Max&#xff08;默认的是对称量化&#xff0c;即不用Z&#xff09;&#xff0c;…

linux 内核中的pid和前缀树

前言&#xff1a; 写这个文章的初衷是因为今天手写了一个字典树&#xff0c;然后写字典树以后忽然想到了之前看的技术文章&#xff0c;linux kernel 之前的pid 申请方式已经从 bitmap 变成了 基数树&#xff0c;所以打算写文章再回顾一下这种数据结构算法 一、内核中pid的申请…

排序算法-基数排序法(RadixSort)

排序算法-基数排序法&#xff08;RadixSort&#xff09; 1、说明 基数排序法与我们之前讨论的排序法不太一样&#xff0c;并不需要进行元素之间的比较操作&#xff0c;而是属于一种分配模式排序方式。 基数排序法比较的方向可分为最高位优先&#xff08;Most Significant Di…

初识容器Docker

目前使用 Docker 基本上有两个选择&#xff1a;Docker Desktop和Docker Engine。Docker Desktop 是专门针对个人使用而设计的&#xff0c;支持 Mac 和 Windows 快速安装&#xff0c;具有直观的图形界面&#xff0c;还集成了许多周边工具&#xff0c;方便易用。 不是太推荐使用D…

【linux kernel】linux内核设备驱动的注册机制

文章目录 1、简介2、driver_register分析&#x1f449;&#xff08;2-1&#xff09;driver_find分析&#x1f449;&#xff08;2-2&#xff09;bus_add_driver分析 3、总结 &#x1f53a;【linux内核系列文章】 &#x1f449;对一些文章内容进行了勘误&#xff0c;本系列文章长…

SpringBoot热部署和整合Mybatis

目录 一、SpringBoot热部署 1.1 添加DevTools依赖 1.2 在idea中设置自动编译 1.3 在Idea设置自动运行 二、SpringBoot整合Mybatis 2.1 准备数据 2.2 添加相关依赖 2.3 在配置文件进行数据源配置 2.4 编写Mapper接口和Mapper文件 2.5 测试 一、SpringBoot热部署 热部…

Spring 事务的简单了解

目录 设计实现 工作原理 简单示例 隔离级别 事务传播机制 Spring框架提供了强大的事务管理支持&#xff0c;它允许开发者以声明性或编程性的方式来管理事务&#xff0c;从而保证数据的一致性和完整性。Spring事务管理的主要特点包括&#xff1a; 声明式事务管理&#xff1…

C语言基础算法复习

003 斐波那契数列问题 #include<stdio.h> int main() {int i,f11,f21,f3,num;printf("%5d %5d",f1,f2);num2;for(i1; i<18; i){f3f1f2;f1f2;f2f3;num;printf("%5d",f3);if(num%40) printf("\n");}return 0; }//#输数斐波那契数列的前20…

k8s-12 存储之configmap

开启之后 先看集群是否正常 Configmap用于保存配置数据&#xff0c;以键值对形式存储configMap 资源提供了向 Pod 注入配置数据的方法旨在让镜像和配置文件解耦&#xff0c;以便实现镜像的可移植性和可复用性 典型的使用场景 填充环境变量的值 设置容器内的命令行参数 填充卷的…