一文整合工厂模式、模板模式、策略模式

为什么使用设计模式

今天终于有时间系统的整理一下这几个设计模式了, 这几个真是最常用的,用好了它们,你就在也不用一大堆的if else 了。能更好的处理大量的代码冗余问题。

在我们的实际开发中,肯定会有这样的场景:我们的某个方法被多次重复调用,但是每次呢,还需要稍微的改动里面一部分的内容。但是我们常常的写法就是,既然能复用,那我就把原来的接口copy出来一份再改一该里面的内容。(哦,nice,真快,三下五除二解决)但是这样的代码其实就真的只是能运行,给后期运维会带来很大的压力。

倘若我们系统第一版本就这么开发完成了,到第二版需求迭代时。
老板说:“把某某某功能改一下,原来的逻辑统一变了,这个就小改动应该很快吧,给你半天时间。”
然后你说:“我靠,那这工作量太大了, 我每个接口都有这个代码,这一变动,就要全部修改,我点需要两天时间。”
老板说:“这么个破修改你要两天,别再这摸鱼了啊,半天肯定能完事,我自己都能改完, 半天改不完,baibai”
然后你默默翻到我的文章,周末两天连续加班,修改了所有代码,全用上了这套设计模式,以后老板给你估算半天修改时间, 你抢答说:“不用,一两个小时吧就够了”。哈哈哈

设计模式介绍

工厂模式(Factory Pattern)是一种创建型设计模式,用于创建对象的实例化逻辑与客户端代码的分离。在工厂模式中,通过工厂类封装实例化对象的过程,客户端代码不需要直接调用构造函数来实例化对象,而是通过工厂类来创建对象。这样可以提供更灵活性和可维护性,且符合开闭原则。工厂模式常见的几种形式包括简单工厂模式、工厂方法模式和抽象工厂模式。

模板模式(Template Pattern)是一种行为设计模式,用于定义算法的骨架,将可变部分延迟到子类中实现。在模板模式中,定义一个模板方法用于规定算法的流程,同时定义一些抽象方法或钩子方法,子类可以重写这些方法以定义具体实现。通过模板模式,可以避免重复的代码,并提供一个统一的算法框架。

策略模式(Strategy Pattern)是一种行为设计模式,用于定义一系列算法,并将每个算法封装成一个独立的策略对象,客户端可以根据需要在运行时选择算法。在策略模式中,算法之间可以互相替换,独立于客户端,并相互独立运行。策略模式可以提高代码的灵活性和复用性,同时符合开闭原则。

实战代码讲解

常规写法

我们就拿最简单最热门的业务,电商项目的支付功能来讲解。
首先我们创建一个支付的接口。

public interface WxPayService {
    void pay(PayParams payParams);
}

public interface AliPayService {
    void pay(PayParams payParams);
}

随便定义一下这个接口参数

@Data
public class PayParams {
    private String name;
}

然后像我们正常的Javaweb项目一样,创建正常的MVC三层结构。

public class WxPayServiceImpl implements PayService{
    // 实现接口中的方法
    @Override
    public void pay(PayParams payParams) {
        // TODO 第一步:检验一些数据参数是否合格

	// TODO 第二步:微信支付具体步骤

	// TODO 第三步:支付成功,进行一些回写、赋值、返回等等操作。
    }
}

public class AliPayServiceImpl implements PayService{
    // 实现接口中的方法
    @Override
    public void pay(PayParams payParams) {
        // TODO 第一步:检验一些数据参数是否合格

	// TODO 第二步:支付宝支付具体步骤

	// TODO 第三步:支付成功,进行一些回写、赋值、返回等等操作。
    }
}

最后再定义一个controller层,调用

@Autowired
private WxPayService wxPayService;

@Autowired
private AliPayService aliPayService;

@PostMapping(value = "/WxPay")
public ResponseResult pay(@RequestBody PayParams params) {
    wxPayService.pay(params);
    return ResponseResult.success();
}

@PostMapping(value = "/AliPay")
public ResponseResult pay(@RequestBody PayParams params) {
    aliPayService.pay(params);
    return ResponseResult.success();
}

这样是实现了我们的支付功能,一个微信支付,一个支付宝支付。但是我们会发现,这样写出来的代码,冗余非常多。

接下来我把这个业务代码修改为使用设计模式的格式。

设计模式后的代码

首先我们还是创建一个支付接口。

public interface PayService {
    void pay(PayParams payParams);
}

然后我们来,实现这个接口,因为我们目前就有两种支付方式,等项目后续升级肯定还会接入越来越多的支付方式,所以我们这里目前就要有两个实现类来实现这个接口, 以后可能更多。

image.png
观看上图我们有发现,在实际的业务逻辑中,其实每个支付的代码中的第一步和第三步都是一样的,所以我们可以把它抽离成一个模板。模板模式这就被使用了。代码如下:

public abstract class PayServiceAbstract implements PayService{
    // 实现接口中的方法
    @Override
    public void pay(PayParams payParams) {
        before(payParams);
        realPay(payParams);
        after(payParams);
    }

    // 把这个核心支付功能定义为抽象的方法,留给具体的策略实现类去实现。
    public abstract void realPay(PayParams payParams);

    // 支付之前、之后的操作在抽象类中就定义一个模板方法,
    // 只要掉用了这个抽象方法就自己有该模板可使用。
    public void before(PayParams payParams){
        System.out.println("支付前操作");
    }
    public void after(PayParams payParams){
        System.out.println("支付后操作");
    }
}

上面代码中,我们是用一个抽象类实现了接口,但是抽象还不是具体的实现,它只是固定了一个模板,接下来我们就要有每个支付方式自己的类去做具体的实现,这个也就是策略模式。对一个接口分别使用不同的策略来做不同的事。

支付宝支付的具体策略服务

@Service
public class AliPayServiceAbstractImpl extends PayServiceAbstract{
    @Override
    public void realPay(PayParams payParams) {
        System.out.println("支付宝支付处理中。。。。");
    }
}

微信支付的具体策略服务

@Service
public class WxPayServiceAbstractImpl extends PayServiceAbstract{
    @Override
    public void realPay(PayParams payParams) {
        System.out.println("微信支付处理中。。。。");
    }
}

ok,到目前,策略模式 + 模板模式就使用成功了,每当需求再叠加时,我们只需要再加一个具体的策略服务就ok了。别的地方都不需要动。

接下来时控制层的调用。我们如果还是用原来的controller,定义一个微信支付接口一个支付宝支付接口,那也太麻烦了。而且如果以后再有个银行卡支付,apple支付,那每次还要新增接口,真麻烦。所以我们不妨使用工厂模式来解决这个问题,让工厂来帮我们确定到底要用哪种支付。这样就来到了我们的工厂模式。

定义一个工厂

@Service
public class PayFactory {
	// payType 哪个具体策略服务
    public PayServiceAbstract getFactory(String payType){
        try {
            Class<?> aClass = Class.forName("com.bihe.controller."+payType);
            PayServiceAbstract payServiceAbstract = (PayServiceAbstract) aClass.getDeclaredConstructor().newInstance();
            return payServiceAbstract;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

上面的代码中,我们是用了Java反射,根据我们传递进来的类名,自动反射创建出具体的策略对象。

最后就是我们的控制层了。

@PostMapping(value = "/pay")
    public ResponseResult pay(@RequestBody PayParams params) {
        PayServiceAbstract payServiceAbstract = payFactory.getFactory(params.getName());
        payServiceAbstract.pay(params);
        return ResponseResult.success();
    }

这就非常简单了, 我们永远只用这一样接口,前端也不需要判断我什么时候调用weixin支付,什么时候调用ali支付…
前端可以一直调用这个一个接口就可以了,只是把响应支付的关键参数传递过来就可以了。

前端调用
image.png

image.png

🆗啦,这样的代码是不是非常的规整,我们后续如果再添加一个新的支付接口就只需要再加一个具体的策略实现类就可以了,如果老板还要修改以前的统一的业务逻辑,那我们也是只需要在模板中也就是抽象类那层修改就可以啦。

哇,代码终于干净了,整个人都舒服了。哈哈😊

查看原文章可进入我的个人博客: 沉思随笔 。欢迎大家🤞

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

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

相关文章

MySQL中char与varchar的区别

文章连接 : MySQL中char与varchar的区别&#xff1a;存储机制、性能差异 | 毛英东的个人博客 (maoyingdong.com) varchar和char在MySQL层的区别 根据MySQL的官方文档The CHAR and VARCHAR Types中的描述, varchar和char的区别主要有&#xff1a; 最大长度&#xff1a;char是2…

基于OneAPI+ChatGLM3-6B+FastGPT搭建LLM大语言模型知识库问答系统

搭建大语言模型知识库问答系统 部署OneAPI部署一个LLM模型部署嵌入模型部署FastGPT新建FastGPT对话应用新建 FastGPT 知识库应用 部署OneAPI 拉取镜像 docker pull justsong/one-api创建挂载目录 mkdir -p /usr/local/docker/oneapi启动容器 docker run --name one-api -d …

官宣了?百度将为苹果今年国行iPhone16、Mac和iOS18提供AI功能!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

Android视角看鸿蒙第九课-鸿蒙的布局

鸿蒙的四大布局 导读 前面八篇文章描述了鸿蒙app的配置文件&#xff0c;关于版本号&#xff0c;开发版本&#xff0c;桌面图标等等配置方式。从这一篇文章开始学习鸿蒙的UI使用方式。 前面我们学习到鸿蒙有ability和page的区分&#xff0c;ability类似Activity但又不完全一样…

目前国内体验最佳的AI问答助手:kimi.ai

文章目录 简介图片理解长文档解析 简介 kimi.ai是国内初创AI公司月之暗面推出的一款AI助手&#xff0c;终于不再是四字成语拼凑出来的了。这是一个非常存粹的文本分析和对话工具&#xff0c;没有那些东拼西凑花里胡哨的AIGC功能&#xff0c;实测表明&#xff0c;这种聚焦是对的…

ESCTF-Web赛题WP

0x01-初次见面-怦然心动:your name? 随便输入一个字 根据提示可以看到 我们需要看源代码 直接 搜索 secret 关键字或者 ESCTF flag ESCTF{K1t0_iS_S0_HAPPy} 0x02-小k的请求 更安全的传参 post 参数为ESCTF 值为 love 自己的ip 同时还有个要求 是需要从度娘转过来 https://www…

说说Loader和Plugin的区别?编写Loader,Plugin的思路?

文章目录 一、区别二、编写loader三、编写plugin参考文献 一、区别 前面两节我们有提到Loader与Plugin对应的概念&#xff0c;先来回顾下 loader 是文件加载器&#xff0c;能够加载资源文件&#xff0c;并对这些文件进行一些处理&#xff0c;诸如编译、压缩等&#xff0c;最终…

KDB+Q | D1 | 学习资源 基础数据类型

官网会是主要的学习资源&#xff1a;https://code.kx.com/q/ 中文教程可能读起来会快一点&#xff1a; https://kdbcn.gitee.io/ 参考了还不错的学习经验帖&#xff1a;https://www.jianshu.com/p/488764d42627 KDB擅长处理时序数据&#xff0c; KDB数据库是后端数据库&…

0基础 三个月掌握C语言(15)

动态内存管理 为什么要有动态内存分配 我们已经掌握的内存开辟⽅式有&#xff1a; int val 20; //在栈空间上开辟四个字节 char arr[10] {0}; //在栈空间上开辟10个字节的连续空间 但上述的开辟空间的⽅式有两个特点&#xff1a; • 空间开辟⼤⼩是固定的。 • 数组…

Visual Studio 小更新:改善变量的可见性

在 Visual Studio 2022 17.10 预览版 2 中&#xff0c;我们改善了一些小功能&#xff0c;例如&#xff1a;在调试版本中&#xff0c;变量窗口现已可以显示调用堆栈中任意帧的局部变量。 如需体验此功能&#xff0c;请直接安装最新预览版本&#xff0c;就可以知道是怎么一回事儿…

复盘一下我用过的设计模式

建造者模式 保卫萝卜中使用了建造者模式。UML图如下&#xff1a; 接口&#xff1a; public interface IBuilder<T> {//获取到游戏物体身上的脚本对象&#xff0c;从而去赋值T GetProductorClass(GameObject gameObject);//使用工厂去获取具体的游戏对象GameObject GetP…

你在测试金字塔的哪一层(下)

​在《你在测试金字塔的哪一层&#xff08;上&#xff09;》中介绍了自动化测试的重要性以及测试金字塔。测试金字塔分为单元测试、服务测试、UI测试&#xff0c;它们分别是什么呢&#xff1f;本期文章让我们一起详细看看测试金字塔的不同层次。 一、单元测试 单元测试是指对程…

猪瘟病毒筛查系统的工作原理

TH-P160S猪瘟病毒筛查系统是一种专门用于检测猪瘟病毒的设备或技术组合&#xff0c;其核心目的是确保生猪养殖、产品流通等环节的安全&#xff0c;防止猪瘟病毒的扩散和传播。猪瘟&#xff0c;又称为“烂肠瘟”&#xff0c;是由黄病毒科瘟毒病属的猪瘟病毒引起猪的一种急性、发…

如何使用PHP和RabbitMQ实现延迟队列(方式二)?

前言 前几天写了一篇关于PHP和RabbitMQ如何通过插件实现延迟队列的功能。 今天写另外一篇不需要插件的方式&#xff0c;使用RabbitMQ的死信队列&#xff08;Dead-Letter-Exchanges, DLX&#xff09;和消息TTL&#xff08;Time-To-Live&#xff09;。 这种方法涉及到设置消息…

3款免费甘特图制作工具的比较和选择指南

GanntProject GanttProject https://www.ganttproject.biz/ 是一款项目管理和调度应用&#xff0c;适用于 Windows、macOS 和 Linux。它易于使用&#xff0c;无需任何设置&#xff0c;适用于个人用户和小型团队。该应用提供任务层次结构和依存关系、里程碑、基准行、Gantt 图表…

Knative 助力 XTransfer 加速应用云原生 Serverless 化

作者&#xff1a;元毅 公司介绍 XTransfer 是一站式外贸企业跨境金融和风控服务公司&#xff0c;致力于帮助中小微企业大幅降低全球展业的门槛和成本&#xff0c;提升全球竞争力。公司连续7年专注 B2B 外贸金融服务&#xff0c;已成为中国 B2B 外贸金融第一平台&#xff0c;目…

20240325,结构嵌套,联合,全局变量,编译预处理和宏,声明

二&#xff0c;结构 2.3 结构中的结构 2.3.1 结构数组 #include<stdio.h>//下一秒 struct time{int hour;int min;int sed; }; struct time timeupdate(struct time now); int main(){struct time testTime[5]{{11,59,59},{12,0,0},{1,29,59},{23,59,59},{19,12,27}…

数据结构 之 队列习题 力扣oj(附加思路版)

优先级队列 #include<queue> --队列 和 优先级队列的头文件 优先级队列&#xff1a; 堆结构 最大堆 和 最小堆 相关函数&#xff1a; front() 获取第一个元素 back() 获取最后一个元素 push() 放入元素 pop() 弹出第一个元素 size() 计算队列中元素…

Maven学习记录

一、简介 1. Maven&#xff1a; 基于 Java 平台的项目管理和整合工具&#xff0c;将项目的开发和管理过程抽象成一个项目对象模型&#xff08;POM&#xff09;。开发人员只需要做一些简单的配置&#xff0c;Maven 就可以自动完成项目的编译、测试、打包、发布以及部署等工作。…

把学浪视频保存到电脑方法

为了可以更好的学习很多用户都会想要将学浪的视频下载下来,但是学浪视频官方却没有提供下载方法,为了将学浪视频下载下来我研究了一段时间,总算有突破,找到了下载方法 文章中所用到的工具就在下面,有需要的自己取一下 链接&#xff1a;https://pan.baidu.com/s/1y7vcqILToULr…