当前位置: 首页 > news >正文

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

为什么使用设计模式

今天终于有时间系统的整理一下这几个设计模式了, 这几个真是最常用的,用好了它们,你就在也不用一大堆的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{// 实现接口中的方法@Overridepublic void pay(PayParams payParams) {// TODO 第一步:检验一些数据参数是否合格// TODO 第二步:微信支付具体步骤// TODO 第三步:支付成功,进行一些回写、赋值、返回等等操作。}
}public class AliPayServiceImpl implements PayService{// 实现接口中的方法@Overridepublic 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{// 实现接口中的方法@Overridepublic 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{@Overridepublic void realPay(PayParams payParams) {System.out.println("支付宝支付处理中。。。。");}
}

微信支付的具体策略服务

@Service
public class WxPayServiceAbstractImpl extends PayServiceAbstract{@Overridepublic 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

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

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

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

相关文章:

  • 什么是通配符SSL证书?
  • Webgl学习系列-认识Webgl
  • 一、TLE9471 - SBC Mode切换 + VCC2 开关
  • 百度谷歌301强引蜘蛛池效果怎么样
  • 项目中配置多个阿里巴巴矢量图库方案
  • SQL-CRUD-1
  • Available platform plugins are: linuxfb, minimal, offscreen, vnc.
  • Centos7.9备份mysql数据库
  • 【YOLOv5改进系列(5)】高效涨点----添加密集小目标检测NWD方法
  • 【Java程序设计】【C00376】基于(JavaWeb)Springboot的社区帮扶对象管理系统(有论文)
  • android各种软件下载
  • 使用API有效率地管理Dynadot域名,使用API进将其他平台的域名转移至dynadot
  • 动态ip白名单频繁更改问题解决方案
  • 代码随想录算法训练营DAY9|C++字符串Part.2|LeetCode:28.实现strStr()、459.重复的子字符串|KMP算法
  • Redis入门到实战-第二十弹
  • SegmentFault for Android 3.0 发布
  • 【翻译】babel对TC39装饰器草案的实现
  • C++类中的特殊成员函数
  • emacs初体验
  • github从入门到放弃(1)
  • HTTP--网络协议分层,http历史(二)
  • Spring Security中异常上抛机制及对于转型处理的一些感悟
  • 表单中readonly的input等标签,禁止光标进入(focus)的几种方式
  • 从地狱到天堂,Node 回调向 async/await 转变
  • 前端面试之CSS3新特性
  • 如何将自己的网站分享到QQ空间,微信,微博等等
  • 如何邀请好友注册您的网站(模拟百度网盘)
  • JavaScript 新语法详解:Class 的私有属性与私有方法 ...
  • Nginx惊现漏洞 百万网站面临“拖库”风险
  • Semaphore
  • 从如何停掉 Promise 链说起
  • ​3ds Max插件CG MAGIC图形板块为您提升线条效率!
  • ​卜东波研究员:高观点下的少儿计算思维
  • ###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯
  • #{} 和 ${}区别
  • #define、const、typedef的差别
  • #NOIP 2014# day.1 生活大爆炸版 石头剪刀布
  • #pragma data_seg 共享数据区(转)
  • #Spring-boot高级
  • #考研#计算机文化知识1(局域网及网络互联)
  • #图像处理
  • (007)XHTML文档之标题——h1~h6
  • (附源码)spring boot公选课在线选课系统 毕业设计 142011
  • (论文阅读31/100)Stacked hourglass networks for human pose estimation
  • (七)c52学习之旅-中断
  • (转)VC++中ondraw在什么时候调用的
  • .net Application的目录
  • .net core 实现redis分片_基于 Redis 的分布式任务调度框架 earth-frost
  • .NET Windows:删除文件夹后立即判断,有可能依然存在
  • .net 发送邮件
  • .net 写了一个支持重试、熔断和超时策略的 HttpClient 实例池
  • .net2005怎么读string形的xml,不是xml文件。
  • .NET和.COM和.CN域名区别
  • ??如何把JavaScript脚本中的参数传到java代码段中
  • @Mapper作用