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

news2024/11/30 0:41:11

一、需求介绍

  现有自己写的的一套审批流程逻辑,由于代码重构,需要把以前的很多业务加上审批的功能,再执行完审批与原有业务之后,生成一个任务,然后再统一处理一个任务(本来是通过数据库作业去处理的,后来说这个任务要马上去处理,只能去统一添加一个处理任务的逻辑,去手动触发作业,心里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;
    //流程id
    private Integer approveId;
    //当前员工id
    private String empId;
    //当前员工name
    private String empName;
    //当前角色ID
    private 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 {
    @Override
    public String execApprove(ApproveDTO approveDTO, Map<String, String> paramMap) {
        System.out.println(approveDTO.getEmpName()+"执行了"+(approveDTO.getAppType().equals(0)?"驳回":"通过")+"审批流程逻辑,因为:"+approveDTO.getSuggestion());
        return paramMap.get("logic");
    }

    @Override
    public 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();
    }
    @Override
    public 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;
    }

    @Override
    protected 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 驳回!");
    }

    @Override
    protected 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 {
    @Override
    public String agreeApprove(Map<String, String> paramMap) {
        System.out.println("执行同意业务逻辑");
        return CommonResult.SUCCESS.getStatus();
    }

    @Override
    public String refuseApprove(Map<String, String> paramMap) {
        System.out.println("执行拒绝业务逻辑");
        return CommonResult.SUCCESS.getStatus();
    }

    @Override
    public String insertApproveTask(Map<String, String> paramMap, Integer approveId) {
        System.out.println("执行插入任务业务逻辑");
        return CommonResult.SUCCESS.getStatus();
    }

    @Override
    public 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 异常
     */
    @Override
    public 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());

    @Test
    public 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注解直接拿到增强对的服务类。

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

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

相关文章

pandas数据分析之数据绘图

一图胜千言&#xff0c;将信息可视化&#xff08;绘图&#xff09;是数据分析中最重要的工作之一。它除了让人们对数据更加直观以外&#xff0c;还可以帮助我们找出异常值、必要的数据转换、得出有关模型的想法等等。pandas 在数据分析、数据可视化方面有着较为广泛的应用。本文…

C# 试图加载格式不正确的程序。 (异常来自 HRESULT:0x8007000B)

C# 在调用Cdll时&#xff0c;可能会出现 &#xff1a;试图加载格式不正确的程序。 (异常来自 HRESULT:0x8007000B)这个错误。 一般情况下是C#目标平台跟Cdll不兼容&#xff0c;64位跟32位兼容性问题&#xff0c; a.客户端调用Cdll报的错则&#xff0c; 1)允许的话把C#客户端…

SQL-basics

SQL 一些常用的查询语句用法 SQL 中的聚合函数 SQL 中的子查询 SQL 使用实例 SELECT F_NAME , L_NAME FROM EMPLOYEES WHERE ADDRESS LIKE ‘%Elgin,IL%’; SELECT F_NAME , L_NAME FROM EMPLOYEES WHERE B_DATE LIKE ‘197%’; SELECT * FROM EMPLOYEES WHERE (SALARY BET…

总结974

今日共计学习12h&#xff0c;日计划完成90%.今晚又把总结时间占用了&#xff0c;明天预留0.5h进行月总结吧&#xff0c;重新制定学习时间表&#xff0c;之前的已经用不了。 跟一个学府的老师聊了聊天&#xff0c;感觉聊完之后&#xff0c;本以为会心情舒畅&#xff0c;没想到反…

ELK安装、部署、调试(五)filebeat的安装与配置

1.介绍 logstash 也可以收集日志&#xff0c;但是数据量大时太消耗系统新能。而filebeat是轻量级的&#xff0c;占用系统资源极少。 Filebeat 由两个主要组件组成&#xff1a;harvester 和 prospector。 采集器 harvester 的主要职责是读取单个文件的内容。读取每个文件&…

同学,您有一张校招绿通卡请查收!

“金三银四”过去&#xff0c;马上“金九银十”了&#xff0c;有实习还没着落的&#xff0c;有在实习但留下成谜的&#xff0c;也有不顾其他只忙秋招的&#xff1b;还有依旧撒网投简历的&#xff1b;以及23 届还在找工作的。 小伙伴们&#xff0c;今年真的好难。 &#xff08…

SQLI-labs-第三关

目录 知识点&#xff1a;单括号)字符get注入 1、判断注入点&#xff1a; 2、判断当前表的字段数 3、判断回显位置 4、爆库名 5、爆表名 6、爆字段名&#xff0c;以users表为例 7、爆值 知识点&#xff1a;单括号)字符get注入 思路&#xff1a; 1、判断注入点&#xff1…

jmeter+nmon+crontab简单的执行接口定时压测

一、概述 临时接到任务要对系统的接口进行压测&#xff0c;上面的要求就是&#xff1a;压测&#xff0c;并发2000 在不熟悉系统的情况下&#xff0c;按目前的需求&#xff0c;需要做的步骤&#xff1a; 需要有接口脚本需要能监控系统性能需要能定时执行脚本 二、观察 >针…

springsecurity+oauth 分布式认证授权笔记总结12

一 springsecurity实现权限认证的笔记 1.1 springsecurity的作用 springsecurity两大核心功能是认证和授权&#xff0c;通过usernamepasswordAuthenticationFilter进行认证&#xff1b;通过filtersecurityintercepter进行授权。springsecurity其实多个filter过滤链进行过滤。…

STM32+RTThread配置以太网无法ping通,无法获取动态ip的问题

记录一个非常蠢的问题&#xff0c;今天在移植rtthread的以太网驱动的时候出现无法获取动态ip的问题&#xff0c;问题如下&#xff1a; 设置为动态ip时不管是连接路由器还是电脑主机都无法ping通&#xff0c;也无法获取dns地址。 设置为静态ip时无法ping通主机。 使用wireshark…

Xilinx-7系列之可配置逻辑块CLB

目录 一、概览 二、CLB结构 三、Slice内部结构 3.1 SliceM结构 3.2 SliceL结构 3.3 查找表LUT 3.4 多路复用器 3.5 存储单元 3.6 进位逻辑 四、应用 4.1 分布式RAM 4.2 ROM(只读存储器) 4.3 Shift Registers&#xff08; 移位寄存器&#xff09; 4.4 存储资源容量…

【【萌新的STM32学习23----数据通信的基本类型】】

萌新的STM32学习23----数据通信的基本类型 数据通信的基本概念 数据通信方式可以分为串行通信&#xff0c;并行通信 串行通信&#xff1a; 数据逐位按顺序依次传输 并行&#xff1a; 数据各位通过多条线同时传输 串行通信&#xff1a; 传输效率低&#xff0c;抗干扰能力强&am…

Milk-V Duo开发板实战——基于MobileNetV2的的图像分类

Milk-V Duo开发板实战——基于MobileNetV2的的图像分类 本教程介绍使用TPU-MLIR工具链对MobileNet-Caffe模型进行转换&#xff0c;生成MLIR以及MLIR量化成INT8模型&#xff0c;并在Milk-V Duo开发板上进行部署测试&#xff0c;完成图像分类任务&#xff0c;涉及以下步骤&#…

ELK安装、部署、调试(三)zookeeper安装,配置

1.准备 java安装&#xff0c;系统自带即可 2.下载zookeeper zookeeper.apache.org上可以下载 tar -zxvf apache-zookeeper-3.7.1-bin.tar.gz -C /usr/local mv apache-zookeeper-3.7.1-bin zookeeper 3.配置zookeeper mv zoo_sample.cfg zoo.cfg /usr/local/zookeeper/con…

界面控件Telerik UI for WPF——Windows 11主题精简模式提升应用体验

Telerik UI for WPF拥有超过100个控件来创建美观、高性能的桌面应用程序&#xff0c;同时还能快速构建企业级办公WPF应用程序。Telerik UI for WPF支持MVVM、触摸等&#xff0c;创建的应用程序可靠且结构良好&#xff0c;非常容易维护&#xff0c;其直观的API将无缝地集成Visua…

ASP.NET修改默认端口

找到发布目录下的appsettings.json文件 加入下面内容 "Kestrel":{"Endpoints": {"Https": {"Url": "https://*:8827"},"Http": {"Url": "http://*:8828"}}} 不使用https的话去掉https,修改…

24.动画魔术菜单指示器

效果 源码 <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Creative CSS Navigation Indicator</title> <link rel="stylesheet" hre…

Linux 内核动态打印调试(dev_info、 dev_dbg )

目录 前言 1 printk消息级别 2 调整内核printk打印级别 3 dev_xxx函数简介 4 配置内核使用动态打印 5 动态调试使用方法 6 动态打印调试的基本原理 &#x1f388;个人主页&#x1f388;&#xff1a;linux_嵌入式大师之路的博客-CSDN博客&#x1f389;&#x1f389;&…

中间件环境搭建配置过程解读

中间件环境搭建 目录 中间件环境搭建xampp 搭建环境Tomcat环境配置安装mysql连接mysql 问题解决 xampp 搭建环境 安装xampp服务集成环境工具 官网地址下载项目压缩包&#xff0c;将项目文件夹放在xampp安装目录的htdocs文件夹下初始化xampp&#xff1a;运行目录内的setup_xamp…

【java】【已解决】IDEA启动报错:Lombok Requires Annotation Processing

解决办法&#xff1a; 1、根据异常提示操作&#xff1a; 直接点击错误提示后面的蓝色标识【Enable】&#xff08;小编点完了所以变灰色&#xff09;&#xff0c;此操作等价于下面的步骤&#xff1a; 【File】-->【Settings】-->【Build】-->【Compiler】-->【Ann…