当前位置: 首页 > 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的用法为&…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;是基于椭圆曲线数学理论的公钥密码系统&#xff0c;由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA&#xff0c;ECC在相同安全强度下密钥更短&#xff08;256位ECC ≈ 3072位RSA…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

数据链路层的主要功能是什么

数据链路层&#xff08;OSI模型第2层&#xff09;的核心功能是在相邻网络节点&#xff08;如交换机、主机&#xff09;间提供可靠的数据帧传输服务&#xff0c;主要职责包括&#xff1a; &#x1f511; 核心功能详解&#xff1a; 帧封装与解封装 封装&#xff1a; 将网络层下发…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...