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

· 浏览次数 : 0

小编点评

**策略模式的实现** 策略模式是一种编程模式,用于解决多个支付渠道的支付请求。在策略模式中,创建一个接口,并在接口中定义每个支付渠道的实现类。然后,我们可以通过调用接口的`getPayment()`方法来获取支付策略,并在使用过程中根据选择的策略处理支付请求。 **策略模式的实现步骤** 1. 创建一个接口 `Payment`,定义其 `getCode()` 和 `doPay()` 方法。 2. 创建多个实现类继承 `Payment` 接口,每个实现类对应不同的支付渠道。 3. 在 `Payment` 中定义 `initRouteMap()` 方法,用于初始化策略映射。 4. 在每个实现类的 `initRouteMap()` 方法中,根据渠道名称加载相应的策略。 5. 创建一个 `PayStrategy` 类,负责加载和管理策略。 6. 在 `PayStrategy` 中定义 `getPayment()` 方法,返回对应的策略。 **示例** ```java // Payment 接口 interface Payment { String getCode(); PayState doPay(String uid, BigDecimal amount); } // JDPay 实现类 class JDPay implements Payment { @Override public String getCode() { return "jdPay"; } @Override public PayState doPay(String uid, BigDecimal amount) { return null; } } ``` **使用策略模式的优点** * 提高代码可维护性 * 避免重复代码 * 提高安全性 **使用策略模式的缺点** * 策略类可能会变得很大 * 客户端需要知道所有支付渠道的代码

正文

故事

“不能在写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、策略类会越来越多,维护成本也会越来越高。

与不得不说,在很多业务中,这种模式用得真的很香相似的内容:

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

title: 不得不说,在很多业务中,这种模式用得真的很香 date: 2024-04-24 22:31:58 permalink: /pages/32af5c/ categories: - 《常用设计模式》笔记 tags: - 策略模式 author: name: 老猫 link: https:/

DevOps|中式土味OKR与绩效考核落地与实践

昨天一个小伙伴和我讨论了一下OKR和绩效管理,所以这次想简单明了地说下在中国怎么做比较合适,很多高大上的理论无法落地也是空中楼阁。 首先说一些,我个人的理解 道德品质和能力素质决定了一个人的职位行为 职位行为决定了业务结果 不同级别/工作性质的人员,绩效考核应该有不同权重组合 团队管理者的绩效不得高

线程池遇到父子任务,有大坑,要注意!

你好呀,我是歪歪。 最近在使用线程池的时候踩了一个坑,给你分享一下。 在实际业务场景下,涉及到业务代码和不同的微服务,导致问题有点难以定位,但是最终分析出原因之后,发现可以用一个很简单的例子来演示。 所以歪师傅这次先用 Demo 说问题,再说场景,方便吸收。 Demo 老规矩,还是先上个代码: 这个

DevOps|研发效能价值如何衡量

现在很多公司都在做或者计划做研发效能,也知道研发效能工作很重要,能提高产研运同学的协同效率,提高员工的工作效率和质量,提高业务交付效率和交付质量,但是价值有多大?效率又有多高呢?因为不容易说清楚,所以经常碰到一些质疑和灵魂拷问。 如何衡量研发效能的效果? 如何衡量研发效能的作用? 如何说清楚研发效能

Docker 中的 .NET 异常了怎么抓 Dump

## 一:背景 ### 1. 讲故事 有很多朋友跟我说,在 Windows 上看过你文章知道了怎么抓 Crash, CPU爆高,内存暴涨 等各种Dump,为什么你没有写在 Docker 中如何抓的相关文章呢?瞧不上吗? 哈哈,在DUMP的分析旅程中,跑在 Docker 中的 .NET 占比真的不多,

增强金蝶云星空的数据分析能力,实现BI 分析功能

小编最近在研究金蝶云星空中如何将已有的BI 工具 集成进去,对于BOS开发毫无经验的我,就这么开始了从0到1的过程。在实现功能过程中,也踩了很多坑,接下来看如何避坑。 那么具体如何实现,根据下面的步骤来看。 话不多说直接上操作步骤,篇幅较长,这是一篇教程贴,分享给需要的用户 1. 开发环境要求 【操

记一次 .NET某账本软件 非托管泄露分析

一:背景 1. 讲故事 中秋国庆长假结束,哈哈,在老家拍了很多的短视频,有兴趣的可以上B站观看:https://space.bilibili.com/409524162 ,今天继续给大家分享各种奇奇怪怪的.NET生产事故,希望能帮助大家在未来的编程之路上少踩坑。 话不多说,这篇看一个.NET程序集泄

一次glide内存泄漏排查分析

glide是一款非常优秀的图片加载框架,目前很多项目在使用。提供了非常方法,在此,笔者就不一一列举了,可以到官网查找。 目前项目在做内存排查,因为是车机项目,之前开发的时候没有注意内存方面的问题(车机项目你懂的),现在ota期间系统提出让我们优化内存,说出现过应用内存一直增加的情况。 一脸懵逼,第一

漫谈Python魔术方法,见过的没见过的都在这里了

漫谈Python魔术方法,见过的没见过的都在这里了 就说一下,不深入 假的一览 提到魔术方法,学过python都应该知道一些。至少你得会__init__吧。 在我之前写的博文中有很多都涉及魔术方法。比如 浅谈Python中的if,可能有你不知道的,涉及__bool__和__len__ 浅谈Pytho

产品经理

学习文章:数观自述|一名密码产品经理的职业修养 产品经理的职业修养 其实做产品就是做人,就和禅修一样,自我修养才是最高境界。很多人在职场的成败得失,最终还是源于个人的为人处世。所以本章就说一些这方面的经验,也许和产品基本技能无关,但可能关乎职业生涯的长期发展。 职业规划很重要 很多人在入行之初,都不