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

news2024/11/13 8:17:41

为什么使用设计模式

今天终于有时间系统的整理一下这几个设计模式了, 这几个真是最常用的,用好了它们,你就在也不用一大堆的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

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

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

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

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

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

相关文章

【C语言基础】:内存操作函数

文章目录 一、memcpy函数的使用和模拟实现1.1 memcpy函数的使用1.2 memcpy函数的模拟实现 二、memmove函数的使用和模拟实现2.1 memmove函数的使用2.2 memmove函数的模拟实现 三、memset函数的使用3.1 menset函数的使用 四、memcmp函数的使用4.1 memcmp函数的使用 学海无涯苦作…

Qt与编码

ASCII码:一个字节&#xff0c;256个字符。 Unicode:字母&#xff0c;汉字都占用两个字节。 utf-8:字母一个字节&#xff0c;汉字3个字节。 gbk:字母一个字节&#xff0c;汉字2个字节。 gb2312:可以表示汉字&#xff0c;gb2312<gbk。 编码查看&#xff1a; https://www.…

钡铼技术R40路由器助力构建无人值守的智能化污水处理厂

钡铼技术R40路由器作为智能化污水处理厂的关键网络设备&#xff0c;发挥着至关重要的作用&#xff0c;助力构建无人值守的智能化污水处理系统。在现代社会&#xff0c;污水处理是城市环境保护和可持续发展的重要组成部分&#xff0c;而智能化污水处理厂借助先进的技术和设备&am…

C语言数据结构易错知识点(5)(插入排序、选择排序)

插入排序&#xff1a;直接插入排序、希尔排序 选择排序&#xff1a;直接选择排序、堆排序 上述排序都是需要掌握的&#xff0c;但原理不会讲解&#xff0c;网上有很多详尽地解释&#xff0c;本文章主要分享一下代码实现上应当注意的事项 1.直接插入排序&#xff1a; 代码实…

Kevin的128纪念日

上面这个是我在三天前做的一个开场白一样的封面。在设计的时候我的想法很简单&#xff0c;把自己给展现出来。我没有去过多的加其他花花绿绿的东西&#xff0c;我想把我本身的状态和形象给凸显出来。 哈哈~看到这里有人就想问&#xff0c;这个躺在沙发上吃零食的懒猫就是你的个…

利用瑞士军刀netcat建立连接并实现文件上传

实验环境&#xff1a; Kali:192.168.117.129 Windows10:192.168.135.142 第一步&#xff1a;建立连接 在Windows上下载netcat(官网搜索) 下载好之后在netcat目录打开cmd进入小黑屏 实验一&#xff1a;建立虚拟机与主机的连接 命令&#xff1a; Kali:nc 192.168.135.144…

FastAPI+React全栈开发05 React前端框架概述

Chapter01 Web Development and the FARM Stack 05 The frontend React FastAPIReact全栈开发05 React前端框架概述 Let’s start with a bit of context here. Perhaps the changes in the world of the web are most visible when we talk about the frontend, the part o…

前端学习之css基本网格布局

网格布局 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>网格布局</title><style>.a{/* grid网格布局 */display: grid;width: 400px;height: 400px;border: 1px solid red;/* 设置当前…

【Spring】IoCDI详解

1. IoC详解 前面提到过IoC就是将对象的控制权交由Spring的IoC容器进行管理&#xff0c;由Spring的IoC容器创建和销毁bean&#xff0c;那么既然涉及到容器&#xff0c;就一定包含以下两方面功能&#xff1a; bean的存储bean的获取 1.1 类注解 Spring框架为了更好地服务应用程…

RabbitMQ3.x之二_RabbitMQ所有端口说明及开启后台管理功能

RabbitMQ3.x之二_RabbitMQ所有端口说明及开启后台管理功能 文章目录 RabbitMQ3.x之二_RabbitMQ所有端口说明及开启后台管理功能1. RabbitMQ端口说明2. 开启Rabbitmq后台管理功能1. 查看rabbitmq已安装的插件2. 开启rabbitmq后台管理平台插件3. 开启插件后&#xff0c;再次查看插…

学习笔记Day15:Shell脚本编程

Shell脚本编程 Linux系统环境 Linux系统的4个主要部分&#xff1a;内核、shell、文件系统和应用程序。 内核是操作系统的核心&#xff0c;决定系统性能和稳定性shell &#xff1a;一种应用程序&#xff0c;是用户和内核交互操作的接口&#xff0c;是套在内核外的壳&#xff…

Linux文件IO(2):使用标准IO进行文件的打开、关闭、读写、流定位等相关操作

目录 前言 文件的打开和关闭的概念 文件的打开 文件的打开函数 文件打开的模式 文件的关闭 文件的关闭函数 注意事项 字符的输入&#xff08;读单个字符&#xff09; 字符输入的函数 注意事项 字符的输出&#xff08;写单个字符&#xff09; 字符输出的函数 注意…

实战|使用 Node.js 和 htmx 构建全栈应用程序

在本教程中&#xff0c;我将演示如何使用 Node 作为后端和 htmx 作为前端来构建功能齐全的 CRUD 应用程序。这将演示 htmx 如何集成到全栈应用程序中&#xff0c;使您能够评估其有效性并确定它是否是您未来项目的不错选择。 htmx 是一个现代 JavaScript 库&#xff0c;旨在通过…

微服务day07 -- 搜索引擎 ( 数据聚合 + 自动补全 + 数据同步 + ES集群 )

1.数据聚合 聚合&#xff08;aggregations&#xff09;可以让我们极其方便的实现对数据的统计、分析、运算。例如&#xff1a; 什么品牌的手机最受欢迎&#xff1f; 这些手机的平均价格、最高价格、最低价格&#xff1f; 这些手机每月的销售情况如何&#xff1f; 实现这些…

ydata_profiling:自动生成数据探索报告的Python库

之前在做数据分析的时候&#xff0c;用过一个自动化生成数据探索报告的Python库&#xff1a;ydata_profiling 一般我们在做数据处理前会进行数据探索&#xff0c;包括看统计分布、可视化图表、数据质量情况等&#xff0c;这个过程会消耗很多时间&#xff0c;可能需要上百行代码…

数据库实验(一)SQL Server触发器

目录 触发器的定义 触发器和存储过程的区别 触发器的优点 触发器的作用 触发器的分类 DML触发器 DDL触发器 登录触发器 触发器的工作原理 inserted表 deleted表 创建触发器 编程要求 测试要求&#xff1a; 实验代码&#xff1a; 触发器的定义 触发器是建立在触…

视频推拉流EasyDSS点播平台云端录像播放异常的问题排查与解决

视频推拉流EasyDSS视频直播点播平台可提供一站式的视频转码、点播、直播、视频推拉流、播放H.265视频等服务&#xff0c;搭配RTMP高清摄像头使用&#xff0c;可将无人机设备的实时流推送到平台上&#xff0c;实现无人机视频推流直播、巡检等应用。 有用户反馈&#xff0c;项目现…

深度学习基础知识

本文内容来自https://zhuanlan.zhihu.com/p/106763782 此文章是用于学习上述链接&#xff0c;方便自己理解的笔记 1.深度学习的网络结构 深度学习是神经网络的一种特殊形式&#xff0c;典型的神经网络如下图所示。 神经元&#xff1a;表示输入、中间数值、输出数值点。例如&…

itextPdf生成pdf简单示例

文章环境 jdk1.8&#xff0c;springboot2.6.13 POM依赖 <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13</version></dependency><dependency><groupId>com.ite…

【前端面试3+1】01闭包、跨域

一、对闭包的理解 定义&#xff1a; 闭包是指在一个函数内部定义的函数&#xff0c;并且该内部函数可以访问外部函数的变量。闭包使得函数内部的变量在函数执行完后仍然可以被访问和操作。 特点&#xff1a; 闭包可以访问外部函数的变量&#xff0c;即使外部函数已经执行完毕。…