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

设计模式的使用——模板方法模式+动态代理模式

一、需求介绍

  现有自己写的的一套审批流程逻辑,由于代码重构,需要把以前的很多业务加上审批的功能,再执行完审批与原有业务之后,生成一个任务,然后再统一处理一个任务(本来是通过数据库作业去处理的,后来说这个任务要马上去处理,只能去统一添加一个处理任务的逻辑,去手动触发作业,心里1w只草泥马在欢快的奔腾着)。现有的问题是:

  • 如何将原有的业务逻辑和审批流程给统一整合,以减少工作量
  • 如何统一添加处理任务的功能

二、设计模式选择

  • 模板方法(Template Method)模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。我们可以根据这个模式去统一整合业务逻辑和审批流程,将整合的逻辑模板里面。
  • 动态代理()模式:通过动态代理模式对生成任务的方法统一增强,在生成这个任务之后,立马去执行这个任务。

三、代码实现

1、准备工作

项目的目录结构如下:

image.png

在开始之前,需要规范一些常量

  • 成功状态表示枚举类:
public enum CommonResult {SUCCESS("200", "success","成功","成功"),FAIL("201","fail","失败","失败");private final String code;private final String status;private final String msg;private final String data;CommonResult(String code, String status, String msg, String data) {this.code = code;this.status = status;this.msg = msg;this.data = data;}public String getCode() {return code;}public String getStatus() {return status;}public String getMsg() {return msg;}public String getData() {return data;}}
  • 新建审批异常类:
public class ApproveException extends RuntimeException {public ApproveException(String message){super(message);}public ApproveException(Throwable throwable){super(throwable);}
}
  • 新建常量类,表示不同审批结果,不同审批返回值需要处理不同的业务逻辑:
public interface Constants {//不执行插入任务 与 执行任务的逻辑String FLOW_STATUS1 = "1";//只执行审批逻辑String FLOW_STATUS2 = "2";//不执行业务逻辑String FLOW_STATUS3 = "3";//执行所有逻辑String FLOW_STATUS4 = "4";
}
  • 封装审批参数类,用来规范审批值传递,通过这个类规定需要审批接口需要的参数:

/*** @description: 请求审批* @create: 2020-04-24 13:40**/
public class ApproveDTO {//审批意见private String suggestion;//0 驳回 1 同意private Integer appType;//流程idprivate Integer approveId;//当前员工idprivate String empId;//当前员工nameprivate String empName;//当前角色IDprivate String roleId;public String getSuggestion() {return suggestion;}public void setSuggestion(String suggestion) {this.suggestion = suggestion;}public Integer getAppType() {return appType;}public void setAppType(Integer appType) {this.appType = appType;}public Integer getApproveId() {return approveId;}public void setApproveId(Integer approveId) {this.approveId = approveId;}public String getEmpName() {return empName;}public void setEmpName(String empName) {this.empName = empName;}public String getEmpId() {return empId;}public void setEmpId(String empId) {this.empId = empId;}public String getRoleId() {return roleId;}public void setRoleId(String roleId) {this.roleId = roleId;}
}

2、编写审批逻辑

  这里只是一个简单地审批服务类,主要有两个功能:
① execApprove()方法:用来执行审批的节点流转等逻辑。
② runApproveTask()方法:这个是后来新加的添加任务后马上执行的需求,主要调用数据库的存储过程。

  • 审批服务接口:
public interface IApproveService {/*** 执行审批* @param approveDTO 审批信息* @return 0 失败 1 成功*/String execApprove(ApproveDTO approveDTO, Map<String,String> paramMap);/*** 执行审批存储过程*/void runApproveTask(Integer approveId);
}
```java
- 审批服务接口实现类
```java
public class ApproveServiceImpl implements IApproveService {@Overridepublic String execApprove(ApproveDTO approveDTO, Map<String, String> paramMap) {System.out.println(approveDTO.getEmpName()+"执行了"+(approveDTO.getAppType().equals(0)?"驳回":"通过")+"审批流程逻辑,因为:"+approveDTO.getSuggestion());return paramMap.get("logic");}@Overridepublic void runApproveTask(Integer approveId) {System.out.println("存储过程被执行......");}
}

3、封装审批模板

  这里主要处理业务逻辑和审批逻辑之间的关系:

  • 抽象类,主要定义了钩子方法要实现的功能,doApprove()方法:执行审批,处理业务逻辑和审批逻辑之间的关系就是这个方法:
public interface IApproveTemplate {String doApprove();}
  • 模板类,通用模板逻辑封装,扩展需要在业务逻辑中实现的功能(抽象方法),实现了IApproveTemplate的doApprove()方法(钩子方法):
/*** @author FluffyCatkin* @create: 2020-06-02 14:00**/
public abstract class CommonApproveTemplate implements IApproveTemplate {protected IApproveService approveService;protected ApproveDTO approveDTO;private final Map<String,String> paramMap;public CommonApproveTemplate(ApproveDTO approveDTO, Map<String,String> paramMap) {this.approveDTO = approveDTO;this.paramMap = paramMap;this.approveService = new ApproveServiceImpl();this.approveService = new ApproveServiceImpl();}@Overridepublic String doApprove(){String execResult = execApprove();String businessResult = "";//执行自定义业务if (Objects.equals(execResult,Constants.FLOW_STATUS1)||Objects.equals(execResult,Constants.FLOW_STATUS4)){businessResult = business();if (!businessResult.equals(CommonResult.SUCCESS.getStatus())){System.out.println("审批流程执行失败,开始回滚");}}//插入任务if (Objects.equals(execResult,Constants.FLOW_STATUS3)||Objects.equals(execResult,Constants.FLOW_STATUS4)){businessResult = taskBusiness();if (!businessResult.equals(CommonResult.SUCCESS.getStatus())){System.out.println("审批流程执行失败,开始回滚");}}//什么也不做if (Objects.equals(execResult,Constants.FLOW_STATUS2)){return CommonResult.SUCCESS.getStatus();}return businessResult;}/*** 执行审批流程节点流转逻辑* @return 不同的执行结果* 比如所有人审批通过或者流转到下一个审批人等等,这里不纠结具体 只用 1 2 3 4表示不同结果*/private String execApprove(){return approveService.execApprove(this.approveDTO,this.paramMap);}/*** 对传的审批参数进行解析校验,可以判断调用通过业务逻辑还是 拒绝的业务逻辑等等*/protected abstract String business();/*** 新增任务,不同的业务需要自定义自己的任务*/protected abstract String taskBusiness();}
  • 业务逻辑抽象类,定义了用户需要在自己的业务服务类中所实现的功能,这些功能不同的业务是不一样的:

public interface CommonApproveService {/*** 跟进审批同意操作* @param paramMap 参数*/String agreeApprove(Map<String, String> paramMap);/*** 跟进审批拒绝操作* @param paramMap 参数*/String refuseApprove(Map<String, String> paramMap);/*** 将审批操作放到任务中跑* @param paramMap 审批参数*/String insertApproveTask(Map<String, String> paramMap,Integer approveId);/*** 执行跟进的审批* @param approveDTO 审批流程改变需要的参数* @param paramMap 执行业务逻辑需要的参数* @return 审批结果*/String execApprove(CommonApproveService service,ApproveDTO approveDTO, Map<String, String> paramMap);}
  • 模板具体实现类,这里封装了用户定义的业务的通用服务类,这些服务类要统一实现上面的CommonApproveService接口:

/*** @author FluffyCatkin* @create: 2020-06-02 14:26**/public class ApproveConcrete<T extends CommonApproveService> extends CommonApproveTemplate {private final T service;private final Map<String, String> paramMap;public ApproveConcrete(ApproveDTO approveDTO, Map<String, String> paramMap, T service) {super(approveDTO, paramMap);this.service = service;this.paramMap = paramMap;}@Overrideprotected String business() {if(super.approveDTO==null){throw new ApproveException("审批失败,没有参数!");}Integer appType = super.approveDTO.getAppType();if (appType==null){throw new ApproveException("审批失败,未传入审批意见,请传入:1 同意  0 驳回!");}if (appType==0){return service.refuseApprove(paramMap);}if (appType==1){return service.agreeApprove(paramMap);}throw new ApproveException("审批失败,审批意见错误,请传入:1 同意  0 驳回!");}@Overrideprotected String taskBusiness() {if(super.approveDTO==null){throw new ApproveException("审批失败,没有参数!");}return service.insertApproveTask(paramMap,approveDTO.getApproveId());}}

4、业务逻辑服务实现

  • 定义服务接口,要继承封装的业务逻辑抽象类
public interface IBusinessService extends CommonApproveService {//在这里可以写业务逻辑相关方法
}
  • 定义服务接口实现类

public class BusinessServiceImpl implements IBusinessService {@Overridepublic String agreeApprove(Map<String, String> paramMap) {System.out.println("执行同意业务逻辑");return CommonResult.SUCCESS.getStatus();}@Overridepublic String refuseApprove(Map<String, String> paramMap) {System.out.println("执行拒绝业务逻辑");return CommonResult.SUCCESS.getStatus();}@Overridepublic String insertApproveTask(Map<String, String> paramMap, Integer approveId) {System.out.println("执行插入任务业务逻辑");return CommonResult.SUCCESS.getStatus();}@Overridepublic String execApprove(CommonApproveService service,ApproveDTO approveDTO, Map<String, String> paramMap) {System.out.println("开始执行审批....");return new ApproveUtil<> (service).execApprove(approveDTO, paramMap);}
}

5、通过动态代理对业务逻辑服务类进行增强,加上立刻执行存储过程的逻辑:

  • 代理类:

/*** @author FluffyCatkin* @version 1.0* @date 2020/1/3 0003 10:04* @description (动态代理)代理(Proxy)类:对真实主题功能的扩展*/
public class DynamicProxy implements InvocationHandler {//需要代理的对象private final Object  dynamicSubject;private final IApproveService approveService = new ApproveServiceImpl();private static final String  PROXY_METHOD = "insertApproveTask";/*** 构造方法* @param dynamicSubject 要代理的对象*/public DynamicProxy(Object dynamicSubject){this.dynamicSubject = dynamicSubject;}/**** @param proxy 被代理的类* @param method 要增强的方法* @param args 增强的方法参数* @return 增强的方法返回值* @throws Throwable 异常*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object invoke = method.invoke(dynamicSubject, args);if (PROXY_METHOD.equals(method.getName())){approveService.runApproveTask(null);}return invoke;}/*** 获取被代理的对象* @return 被代理的对象*/public Object getCommonApproveService() {return dynamicSubject;}/*** 获取代理后增强的对象* @param dynamicSubject 被代理的对象* @return 代理后增强的对象*/public static  CommonApproveService newInstance(Object dynamicSubject) {InvocationHandler invocationHandler = new DynamicProxy(dynamicSubject);return (CommonApproveService) Proxy.newProxyInstance(dynamicSubject.getClass().getClassLoader(),dynamicSubject.getClass().getInterfaces(),invocationHandler);}
}

6、测试


public class MainTest {//对业务的服务类进行增强,在spring中,可以通过spring配置,获取所有CommonApproveService的子类,并统一进行增强//然后通过@Autowired注解直接拿到增强对的服务类private final CommonApproveService businessService = DynamicProxy.newInstance(new BusinessServiceImpl());@Testpublic void testPassApprove() {ApproveDTO approveDTO = new ApproveDTO();approveDTO.setAppType(1);approveDTO.setEmpName("张三");approveDTO.setSuggestion("你长得太好看了");//这里是业务执行需要的参数,不同的业务需要不同的参数,这里使用map封装Map<String, String> paramMap = new HashMap<>();//通过这里指定审批流程的流转结果,从而控制其流转到不同的业务逻辑里,实际情况是根据审批节点流转结果判断的
//        paramMap.put("logic", Constants.FLOW_STATUS1);
//        paramMap.put("logic", Constants.FLOW_STATUS2);
//        paramMap.put("logic", Constants.FLOW_STATUS3);paramMap.put("logic", Constants.FLOW_STATUS4);String res = businessService.execApprove(businessService, approveDTO, paramMap);System.out.println(res);}
}

执行结果:
image.png

  注意:这里简化了很多逻辑,也可以结合项目进行改造,比如:可以在spring中,可以通过spring配置,获取所有CommonApproveService的子类,并统一进行增强,然后通过@Autowired注解直接拿到增强对的服务类。

相关文章:

设计模式的使用——模板方法模式+动态代理模式

一、需求介绍 现有自己写的的一套审批流程逻辑&#xff0c;由于代码重构&#xff0c;需要把以前的很多业务加上审批的功能&#xff0c;再执行完审批与原有业务之后&#xff0c;生成一个任务&#xff0c;然后再统一处理一个任务&#xff08;本来是通过数据库作业去处理的&#x…...

C++学习记录——삼십 智能指针

文章目录 1、为什么需要智能指针&#xff1f;2、内存泄漏3、智能指针的使用及原理1、RAII思想2、拷贝问题1、unique_ptr2、shared_ptr1、多线程2、循环引用3、定制删除器 1、为什么需要智能指针&#xff1f; 看一个场景 int div() {int a, b;cin >> a >> b;if (b…...

插件式架构 与 ReSharper、Visual Studio的故事

文章首发地址 ReSharper和Visual Studio的故事 ReSharper是一款由JetBrains公司开发的Visual Studio插件&#xff0c;它主要用于提高Visual Studio的开发效率和改善代码质量。ReSharper在早期的版本中被称为"Omea Code"&#xff0c;它最初是JetBrains一个研究项目的…...

Python UDP编程

前面我们讲了 TCP 编程&#xff0c;我们知道 TCP 可以建立可靠连接&#xff0c;并且通信双方都可以以流的形式发送数据。本文我们再来介绍另一个常用的协议--UDP。相对TCP&#xff0c;UDP则是面向无连接的协议。 UDP 协议 我们来看 UDP 的定义&#xff1a; UDP 协议&#xff…...

结构体(个人学习笔记黑马学习)

1、结构体的定义和使用 #include <iostream> using namespace std; #include <string>struct Student {string name;int age;int score; }s3;int main() {//1、struct Student s1;s1.name "张三";s1.age 18;s1.score 100;cout << "姓名&a…...

小白带你学习linux的PXE装机

目录 目录 一、PXE是什么&#xff1f; 二、PXE的组件&#xff1a; 1、vsftpd/httpd/nfs 2、tftp 3、dhcp 三、配置dhcp 1、关闭防火墙与selinux和配置本地yum源 2、安装dhcp服务 3、配置dhcp配置文件 四、配置vsftpd 五、配置tftp 1、安装tftp-server 2、启动tft…...

华为鲲鹏服务器

1.简介 鲲鹏通用计算平台提供基于鲲鹏处理器的TaiShan服务器、鲲鹏主板及开发套件。硬件厂商可以基于鲲鹏主板发展自有品牌的产品和解决方案&#xff1b;软件厂商基于openEuler开源OS以及配套的数据库、中间件等平台软件发展应用软件和服务&#xff1b;鲲鹏开发套件可帮助开发…...

Python金币小游戏

游戏规则&#xff1a;移动挡板接住金币 游戏截图&#xff1a; 详细代码如下&#xff1a; import pygame.freetype import sys import randompygame.init() screen pygame.display.set_mode((600, 400)) pygame.display.set_caption(game) p 0 i1 0 s 0 t 0 f1 pygame.f…...

Modbus转Profinet网关在大型自动化仓储项目应用案例

在自动化仓储项目中&#xff0c;Modbus是一种常见的通信协议&#xff0c;用于连接各种设备&#xff0c;例如传感器、PLC和人机界面。然而&#xff0c;Modbus协议只支持串行通信&#xff0c;并且数据传输速度较慢。为了提高通信效率和整体系统性能&#xff0c;许多大型仓储项目选…...

Java 并发 ThreadLocal 详解

文章首发于个人博客&#xff0c;欢迎访问关注&#xff1a;https://www.lin2j.tech 简介 ThreadLocal 即线程本地变量的意思&#xff0c;常被用来处理线程安全问题。ThreadLocal 的作用是为多线程中的每一个线程都创建一个线程自身才能用的实例对象&#xff0c;通过线程隔离的…...

JWT 技术的使用

应用场景&#xff1a;访问某些页面&#xff0c;需要用户进行登录&#xff0c;那我们如何知道用户有没有登录呢&#xff0c;这时我们就可以使用jwt技术。用户输入的账号和密码正确的情况下&#xff0c;后端根据用户的唯一id生成一个独一无二的token&#xff0c;并返回给前端&…...

机器学习深度学习——NLP实战(自然语言推断——微调BERT实现)

&#x1f468;‍&#x1f393;作者简介&#xff1a;一位即将上大四&#xff0c;正专攻机器学习的保研er &#x1f30c;上期文章&#xff1a;机器学习&&深度学习——针对序列级和词元级应用微调BERT &#x1f4da;订阅专栏&#xff1a;机器学习&&深度学习 希望文…...

如何在windows下使用masm和link对汇编文件进行编译

前言 32位系统带有debug程序&#xff0c;可以进行汇编语言和exe的调试。但真正的汇编编程是“编辑汇编程序文件(.asm)->编译生成obj文件->链接生成exe文件”。下面&#xff0c;我就来说一下如何在windows下使用masm调试&#xff0c;使用link链接。 1、下载相应软件 下载…...

Golang字符串基本处理方法

Golang的字符串处理 字符串拼接 两种方法&#xff1a;strings.Join方法和’方法 package mainimport ("fmt""strings" )func main() {num : 20strs : make([]string, 0)for i : 0; i < num; i {strs append(strs, "fht")}//string.join拼…...

算法训练营第三十九天(8.30)| 动态规划Part09:购买股票

Leecode 123.买卖股票的最佳时机 III 123.买卖股票的最佳时机III 123.买卖股票的最佳时机III 题目地址&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目类型&#xff1a;股票问题 class Solution { public:int maxProfit(vector<…...

renren-fast-vue环境升级后,运行正常打包后,访问页面空白

网上各种环境&#xff0c;路径都找了一遍&#xff0c;也没成功。后来发现升级后打包的dist文件结构发生了变化&#xff0c; 1.最开始正常版本是这样 2.升级后是这样&#xff0c;少了日期文件夹 3.问题&#xff1a;打包后的index.html中引入的是config文件夹&#xff0c;而打…...

Uniapp笔记(三)uniapp语法2

一、本节项目预备知识 1、组件生命周期 1.1、什么是生命周期 生命周期(Life Cycle)是指一个对象从创建-->运行-->销毁的整个阶段&#xff0c;强调的是一个时间段 我们可以把每个uniapp应用运行的过程&#xff0c;也概括为生命周期 小程序的启动&#xff0c;表示生命周…...

windows【ftp-FTP】添加配置流程【iis服务】

第一步&#xff1a;自己安装iis服务和ftp服务【自己百度搜索】 第二步&#xff1a;添加ftp站点【配置主动端口默认为21】 第三方配置&#xff1a;ftp被动端口【这里设置为3000-4000】请在防火墙开放此端口【如果是阿里云请在阿里云的后天也开通此端口】【护卫神一般使用55000…...

mysql视图的创建和选项配置详解

在 MySQL 中&#xff0c;可以使用 CREATE VIEW 语句来创建视图。基本的语法如下&#xff1a; CREATE[OR REPLACE][ALGORITHM {UNDEFINED | MERGE | TEMPTABLE}][DEFINER {user | CURRENT_USER}][SQL SECURITY { DEFINER | INVOKER }]VIEW view_name [(column_list)]AS selec…...

Python正则表达式中re.sub自定义替换方法正确使用方法

大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 话不多说&#xff0c;直接开搞&#xff0c;如果有什么疑惑/资料需要的可以点击文章末尾名片领取源码 在使用正则替换时&#xff0c;有时候需要将匹配的结果做对应处理&#xff0c;便可以使用自定义替换方法。 re.sub的用法为&…...

RestClient

什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端&#xff0c;它允许HTTP与Elasticsearch 集群通信&#xff0c;而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级&#xff…...

RocketMQ延迟消息机制

两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数&#xff0c;对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后&#xf…...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

HTML 列表、表格、表单

1 列表标签 作用&#xff1a;布局内容排列整齐的区域 列表分类&#xff1a;无序列表、有序列表、定义列表。 例如&#xff1a; 1.1 无序列表 标签&#xff1a;ul 嵌套 li&#xff0c;ul是无序列表&#xff0c;li是列表条目。 注意事项&#xff1a; ul 标签里面只能包裹 li…...

反射获取方法和属性

Java反射获取方法 在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时访问和操作类的内部属性和方法。通过反射&#xff0c;可以动态地创建对象、调用方法、改变属性值&#xff0c;这在很多Java框架中如Spring和Hiberna…...

unix/linux,sudo,其发展历程详细时间线、由来、历史背景

sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...