不得不说,在很多业务中,这种设计模式用得真的很香

故事

“不能在写if else来拓展当前系统了,现在已经有三个支付场景了…”工位上,小猫看着电脑,挠着头。

就在刚刚,小猫接到了一个新需求,需要和客户公司打通资产,形成资产联动。说白了就是需要定制化对接客户公司的支付资产体系。除了这次接到的之外。前面其实已经对接了三家了。由于每家对接规范都不一样,历史对接的时候为了尽快上线,都是直接搞个else的新路由分支,然后去实现支付,退款。

在小猫看来,就是在堆屎山。牵一发而动全身的感觉真的很不好。由于本次的需求留有的时间还是相当充裕的,所以小猫下定决心,打算利用这次的拓展,将原来不合理的地方用上设计模式将其重构掉。

深思熟虑很久,小猫下定决心打算用“策略模式”重构一番。

聊聊策略模式

说到策略模式,老猫觉得这种设计模式在实际开发中使用其实是相当频繁的。老猫工作到现在也在很多业务场景中使用过这样的设计模式。例如,上述小猫遇到的第三方支付集成的问题上。另外的还有商城搞活动,针对不同的用户下单行为提供不同的折扣或者返现等活动。再例如商城运营人员根据不同的加价策略去定在售商品的价格等。

老猫工作十年中,对接过很多外部企业或者单位的接口,若业务定义一样,只是接口协议不同的业务其实往往都可以用到策略模式。
提炼一下适用场景如下:

(1)系统中有很多类,而它们的区别仅仅在于行为不同。

(2)一个系统需要动态地在几种算法中选择一种。

在很多业务中,这种模式用起来真的很香,既能够摆脱成堆的“if else”(当然关于 if else的优化,又是另外一个故事了,有兴趣的小伙伴可以看看这篇文章【接手了个项目,被if…else搞懵逼了】),另外写出来的代码本身拓展性也会比较好。

那么我们来看看策略模式,并且基于小猫遇到的场景问题,咱们来撸一下实现代码。

策略模式解决多路支付通道问题

在定义支付行为的时候,我们首先定义出常规的支付行为,咱们可以用接口interface的形式定义出来,当然也可以用abstract类的方式定义出来。这里老猫使用后者来定义。代码如下:

/**
 * @author 公众号:程序员老猫
 */
public abstract class Payment {
    //获取支付渠道的名称
    public abstract String getName();

    //查询用户余额
    protected abstract BigDecimal queryBalance(String uid);

    public PayState doPay(String uid, BigDecimal amount) {
        if (queryBalance(uid).compareTo(amount) < 0) {
            return new PayState(500, "支付失败", "账户余额不足");
        }
        return new PayState(200, "支付成功", "支付金额:" + amount);
    }
}

定义一个标准的支付状态类:

/**
 * @author 公众号:程序员老猫
 */
public class PayState {
    private int code;
    private String msg;
    private Object data;

    public PayState(int code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public String toString() {
        return ("pay state :[" + code + "]," + msg + ",order detail: " + data);
    }
}

接下来,咱们来模拟各个支付渠道,并且咱们能够知道在不同的支付渠道中,我们当前的账户余额是多少。咱们就拿用的比较多的微信、支付宝、京东支付等支付渠道来做模拟吧。

支付宝实现,并且账户中有900元:

public class AliPay extends Payment {
    @Override
    public String getName() {
        return "支付宝";
    }

    @Override
    protected BigDecimal queryBalance(String uid) {
        return new BigDecimal(900);
    }
}

微信支付,并且账户中有300元:

public class WxPay extends Payment{
    @Override
    public String getName() {
        return "微信";
    }

    @Override
    protected BigDecimal queryBalance(String uid) {
        return new BigDecimal(300);
    }
}

以此类推,京东支付。

public class JDPay extends Payment{
    @Override
    public String getName() {
        return "京东白条";
    }

    @Override
    protected BigDecimal queryBalance(String uid) {
        return new BigDecimal(400);
    }
}

定义好各种单一支付通道之后,其实我们就要组装策略了。把上述支付通道,加载到策略路由类中。老猫觉得这个地方也是策略模式中比较核心的点。

/**
 * @author 公众号:程序员老猫
 */
public class PayStrategy {
    public static final String ALI_PAY = "aliPay";
    public static final String WX_PAY = "wxPay";
    public static final String JD_PAY = "jdPay";
    public static final String DEFAULT = "wxPay";

    //初始化的时候装载支付行为策略
    private static Map<String,Payment> paymentMap = new HashMap<>();

    static {
        paymentMap.put(ALI_PAY,new AliPay());
        paymentMap.put(WX_PAY,new WxPay());
        paymentMap.put(JD_PAY,new JDPay());
        paymentMap.put(DEFAULT,new WxPay());
    }

    //调用的时候路由具体的支付策略
    public static Payment get(String payKey){
        if(!paymentMap.containsKey(payKey)){
            return paymentMap.get(DEFAULT);
        }
        return paymentMap.get(payKey);
    }
}

接下来,我们就模拟用户下订单支付行为了,具体如下:

/**
 * @author 程序员老猫
 * 下单场景
 */
public class Order {
    private String uid; //用户Id
    private String orderId; //订单Id
    private BigDecimal orderAmount; //支付金额

    public Order(String uid, String orderId, BigDecimal orderAmount) {
        this.uid = uid;
        this.orderId = orderId;
        this.orderAmount = orderAmount;
    }

    public PayState doPay() {
        return doPay(PayStrategy.DEFAULT);
    }

    public PayState doPay(String payKey) {
        Payment payment = PayStrategy.get(payKey);
        System.out.println("欢迎使用" + payment.getName());
        System.out.println("本次交易金额:" + orderAmount);
        return payment.doPay(uid, orderAmount);
    }
}

最终咱们来进行测试一下:

public class PayStrategyTest {
    public static void main(String[] args) {
        Order order = new Order("ktdaddy","20240425224901",new BigDecimal(245));

        System.out.println(order.doPay(PayStrategy.ALI_PAY));
    }
}

结果输出:

欢迎使用支付宝
本次交易金额:245
pay state :[200],支付成功,order detail: 支付金额:245

上述基本就是策略模式的使用了。老猫觉得应该还是比较清晰的。咱们简单看一下最终的调用类图:

在这里插入图片描述

到这里很多小伙伴可能会问了,上面写的案例其实并没有结合我们实际的spring开发框架去实现策略模式,日常开发的过程中我们Java程序员主要用的还是spring框架。那么如果要结合咱们spring日常开发框架又是怎么去实现呢。那么接下来,咱们接着往下看。

SpringBoot下策略模式解决多路支付通道

其实核心的思想还是上面这几个要领,老猫在此不多做展开,只是给大家提供一些思路,然后提供一些简单的日常开发中使用的截图给大家参考。支付使用策略模式的核心的思想无非就下面两个。

(1)咱们需要不同的支付策略类。

(2)需要有路由支付策略类的路由类。

其实上面两个核心中,比较重要的还是第二点,咱们如果去初始化策略类。在上面案例中,老猫使用的静态方法块来装载各个策略方法。在spring中其实我们可以使用@PostConstruct注解,进行service策略的初始化装载。

如下首先定义一个标准的支付接口,并且实现一下:

public interface Payment {
    //获取支付渠道的名称
    String getCode();

    PayState doPay(String uid, BigDecimal amount);
}

然后实现这个接口,咱们举一个例子来说明

@Service
public class JDPay implements Payment {

    @Override
    public String getCode() {
        return "jdPay";
    }

    @Override
    public PayState doPay(String uid, BigDecimal amount) {
        return null;
    }
}

关键此时咱们看一下核心加载的地方。

/**
 * 程序员老猫
 **/
@Service
public class PayStrategy {
    @Autowired
    private Payment[] payments;

    //初始化的时候装载支付行为策略
    private static Map<String, Payment> paymentMap = new ConcurrentHashMap<>();


    @PostConstruct
    private void initRouteMap() {
        for (Payment externalPayService : payments) {
            paymentMap.put(externalPayService.getCode(), externalPayService);
        }
    }

    public Payment getPayment(String payCode) {
        return paymentMap.get(payCode);
    }
}

上述就是结合spring的核心策略模式的实现方式,老猫这里没有展开,但是最精华的部分,老猫觉得已经说清楚了。当然基于@PostConstruct进行策略加载的方式只是一种。大家可以实现spring自带的InitializingBean,在 Spring 容器完成 bean 的属性注入后,会调用 afterPropertiesSet() 方法来执行初始化逻辑。

总结

上述主要和大家分享了基于策略模式如何去做支付整合第三方支付的问题。当然这只是一个简单的案例,其实很多时候我们在实际的业务开发中很多地方都可以用到这样一个模式。在jdk源码中以及spring源码中也屡见不鲜。但是策略模式也不是万能的,存在优点的同时也存在缺点。

优点:

1、策略模式符合开闭原则。(当然有兴趣了解设计原则的小伙伴欢迎戳【违反这些设计原则,系统就等着“腐烂”】)

2、策略模式可以避免使用多重复的条件语句。例如优化if else。当然之前也老猫也写过类似博文。【接手了个项目,被if…else搞懵逼了】

3、使用策略模式可以提高算法的保密性和安全性。

缺点:

1、不像适配器模式,策略模式要求客户端需要知道所有的策略,并且自行决定使用哪类策略。关于适配器模式,感兴趣的小伙伴可以看这里【真香定律!我用这种模式重构了第三方登录】

2、策略类会越来越多,维护成本也会越来越高。

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

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

相关文章

Linux下基本指令-掌握

目录 为什么要学命令行 Linux下基本指令-掌握 ls 指令 pwd命令 cd 指令 touch指令 mkdir指令&#xff08;重要&#xff09;&#xff1a; rmdir指令 && rm 指令&#xff08;重要&#xff09;&#xff1a; man指令&#xff08;重要&#xff09;&#xff1a; cp指…

微信黑名单怎么恢复?一招迅速搞定

“求助&#xff01;微信拉黑后&#xff0c;怎样找到并解除黑名单&#xff1f;我不知道具体的操作&#xff0c;希望可以分析给我详细的图文解说&#xff0c;感谢&#xff01;” 微信的黑名单功能允许用户将某人加入黑名单&#xff0c;从而屏蔽其发送消息、查看朋友圈等行为。然…

7天入门Android开发之第1天——初识Android

一、Android系统 1.Linux内核层&#xff1a; 这是安卓系统的底层&#xff0c;它提供了基本的系统功能&#xff0c;如内存管理、进程管理、驱动程序模型等。安卓系统构建在Linux内核之上&#xff0c;借助于Linux的稳定性和安全性。 2.系统运行库层&#xff1a; 这一层包括了安卓…

最新windows版本erlang26.0和rabbitmq3.13下载

Erlang下载 官网下载&#xff1a;https://www.erlang.org/patches/otp-26.0 百度网盘&#xff1a;https://pan.baidu.com/s/1xU4syn14Bh7QR-skjm_hOg 提取码&#xff1a;az1t RabbtitMQ下载 官网下载&#xff1a;https://www.rabbitmq.com/docs/install-windows 百度网盘…

一文解读:阿里云 AI 基础设施的演进与挑战

云布道师 2024 年 4 月 18-19 日&#xff0c;2024 中国生成式 AI 大会在北京 JW 万豪酒店举行&#xff0c;阿里云高级技术专家、阿里云异构计算 AI 推理团队负责人李鹏受邀在【AI Infra】专场发表题为《AI 基础设施的演进与挑战》的主题演讲。李鹏从 AIGC 对云基础设施的挑战、…

echarts利用graphic属性给饼图添加内圈图片及外圈图片(可自适应宽度位于饼图中心)

最终效果图&#xff1a; 目录 前言资源主要部分graphic介绍style介绍代码 加载饼图方法&#xff08;option所有的配置&#xff09; 前言 思路是看到这个博客启发的&#xff1a;点击跳转查看博客&#xff0c;然后在graphic属性里改了我的实际需求&#xff0c;譬如图片的宽高、…

【笔试强训】除2!

登录—专业IT笔试面试备考平台_牛客网牛客网是互联网求职神器&#xff0c;C、Java、前端、产品、运营技能学习/备考/求职题库&#xff0c;在线进行百度阿里腾讯网易等互联网名企笔试面试模拟考试练习,和牛人一起讨论经典试题,全面提升你的技术能力https://ac.nowcoder.com/acm/…

如何安装sbt(sbt在ubuntu上的安装与配置)(有详细安装网站和图解)

sbt下载官网 选择对应的版本和安装程序 Download | sbt (scala-sbt.org) 安装 解压 将sbt-1.9.0.tgz上传到xshell&#xff0c;并解压 解压&#xff1a; tar -zxvf sbt-1.9.0.tgz 配置 1、在/home/hadoop/sbt中创建sbt脚本 /home/hadoop/sbt 注意要改成自己的地址 cd …

16 JavaScript学习: 类型转换

JavaScript 类型转换 Number() 转换为数字&#xff0c; String() 转换为字符串&#xff0c; Boolean() 转换为布尔值。 JavaScript 数据类型 在 JavaScript 中有 6 种不同的数据类型&#xff1a; stringnumberbooleanobjectfunctionsymbol 3 种对象类型&#xff1a; Obje…

Springboot多数据源及事务实现方案

Springboot多数据源及事务实现方案 文章目录 Springboot多数据源及事务实现方案背景问题分析实现原理1. 数据源抽象与动态路由2. 线程本地存储&#xff08;ThreadLocal&#xff09;3. 面向切面编程&#xff08;AOP&#xff09;4. 自定义注解 实现流程1. 设置数据源标识2. 开始数…

Godot3D学习笔记1——界面布局简介

创建完成项目之后可以看到如下界面&#xff1a; Godot引擎也是场景式编程&#xff0c;这里的一个场景相当于一个关卡。 这里我们点击左侧“3D场景”按钮创建一个3D场景&#xff0c;现在在中间的画面中会出现一个球。在左侧节点视图中选中“Node3D”&#xff0c;右键创建子节点…

医院手术室麻醉信息管理系统源码 自动生成麻醉的各种医疗文书(手术风险评估表、手术安全核查表)

目录 手术风险评估表 一、患者基本信息 二、既往病史 三、手术相关信息 四、风险评估因素 五、风险评估结果 手术安全核查表 一、患者身份与手术信息核对 二、术前准备核查 三、手术团队与职责确认 四、手术物品与设备核查 五、术中关键步骤核查 六、术后核查 七…

STM32中断实现旋转编码器计数

系列文章目录 STM32单片机系列专栏 C语言理论和实践总结专栏 文章目录 1. 旋转编码器 2. 中断代码编写 2.1 Interrupt.c 2.2 Interrupt.h 2.3 完整工程文件 1. 旋转编码器 旋转编码器主要用于测量轴的旋转位置、速度或者是角度的变化&#xff0c;它能够将转动的角度或者…

新兴游戏引擎Godot vs. 主流游戏引擎Unity和虚幻引擎,以及版本控制工具Perforce Helix Core如何与其高效集成

游戏行业出现一个新生事物——Godot&#xff0c;一个免费且开源的2D和3D游戏引擎。曾经由Unity和虚幻引擎&#xff08;Unreal Engine&#xff09;等巨头主导的领域如今迎来了竞争对手。随着最近“独特”定价模式的变化&#xff0c;越来越多的独立开发者和小型开发团队倾向于选择…

【数据结构】反转链表

给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 Definition for singly-linked list.struct ListNode {int val;struct ListNode *next;};typedef struct ListNode ListNode; struct ListNode* reverseList(struct ListNode* head) {i…

JavaEE初阶——文件操作和IO

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 此篇文章与大家分享文件操作及IO的内容 如果有不足的或者错误的请您指出! 目录 *1.解释IO**2.关于文件的基本知识*2.1路径2.1.1绝对路径2.1.2相对路径 2.2文件分类 *3.通过Java代码操作文件*3.1针…

Arcpy入门笔记(三):数据属性的读取

Arcpy入门笔记&#xff08;三&#xff09;&#xff1a;数据属性的获取 文章目录 Arcpy入门笔记&#xff08;三&#xff09;&#xff1a;数据属性的获取常用的属性Describe对象属性&#xff08;部分&#xff09;数据集属性&#xff08;部分&#xff09;表属性&#xff08;部分&a…

python 脚本头(PyCharm+python头部信息、py头部信息、python头信息、py头信息、py文件头部)

文章目录 参考PyCharm设置脚本头头部信息 参考 https://developer.aliyun.com/article/1166544 https://blog.csdn.net/Dontla/article/details/131743495 https://blog.csdn.net/dongyouyuan/article/details/54408413 PyCharm设置脚本头 打开pycharm&#xff0c;点击file–…

5G赋能 扬帆未来|AGV无人仓成黑科技“顶流”

AGV 近年来&#xff0c;无人化这个概念逐渐被运用到了社会中的各个行业&#xff0c;而跟物流有关的就有无人分拣机器人、无人驾驶卡车、和无人叉车&#xff0c;越来越多的新装备也开始投入到实际运用中。 仓储管理在物流管理中占据着核心地位。传统的仓储管理中存在诸多的弊端…

怎样选购内衣洗衣机?2024年5款最新推荐机型种草

随着科技的不断发展&#xff0c;内衣洗衣机成为了家家户户必备的小家电之一&#xff0c;为我们的生活带来了极大的便利。但面对市场上众多的内衣洗衣机品牌&#xff0c;如何选择一款质量好的内衣洗衣机呢&#xff1f;本文将为您推荐5款最新的内衣洗衣机品牌&#xff0c;从而帮助…