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

一、需求介绍

  现有自己写的的一套审批流程逻辑,由于代码重构,需要把以前的很多业务加上审批的功能,再执行完审批与原有业务之后,生成一个任务,然后再统一处理一个任务(本来是通过数据库作业去处理的,后来说这个任务要马上去处理,只能去统一添加一个处理任务的逻辑,去手动触发作业,心里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注解直接拿到增强对的服务类。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

项目开发尺寸像素问题

一般来说 项目开发开始会固定页面尺寸 一般都是1920x1080像素 那这样开发是最简单的我们直接用设计图的像素来开发就行 不用管有什么滚动条 一般浏览器都会有边框什么的 所以会出现滚动条 当我们全屏状态下其实才是我们想要页面结果 如果我们想没有滚动条开发 首先我们要知道…

rke安装k8s

1、修改集群中各物理机主机名hostname文件 # 查看 cat /etc/hostname # 命令修改 hostnamectl set-hostname k8s-master2、实现主机名与ip地址解析 # 查看cat /etc/hosts # 修改 vi /etc/hosts3、配置ip_forward过滤机制 # 修改 vi /etc/sysctl.conf net.ipv4.ip_forward1…

使用element-ui中的el-table回显已选中数据时toggleRowSelection报错

最近在写一个后台&#xff0c;需要在表格中多选&#xff0c;然后点击编辑按钮的时候&#xff0c;需要回显已经选中的表单项 <el-table v-loading"loading" :data"discountList" :row-key"(row) > row.id" refmultipleTable selection-cha…

【pyqt5界面化工具开发-11】界面化显示检测信息

目录 0x00 前言&#xff1a; 一、布局的设置 二、消息的显示 0x00 前言&#xff1a; 我们在10讲的基础上&#xff0c;需要将其输出到界面上 思路&#xff1a; 1、消息的传递 2、布局的设置 先考虑好消息的传递&#xff0c;再来完善布局 其实先完善布局&#xff0c;再来设置消…

树多选搜索查询,搜索后选中状态仍保留

<template><div class"half-transfer"><div class"el-transfer-panel"><div><el-checkbox v-model"selectAll" change"handleSelectAll">全部</el-checkbox></div><el-input v-model&qu…

Breakpad交叉编译aarch64

原本是在mac os上编译的&#xff0c;但是会出现一些问题&#xff0c;因此还是开了台linux容器进行编译&#xff0c;本文可结合之前的linux下编译breakpad共同查看。 Linux下Breakpad编译_Edward.W的博客-CSDN博客https://blog.csdn.net/u013379032/article/details/130754480?…

北京985学校,交叉学科考英一数三408

北京师范大学(B) 考研难度&#xff08;☆☆☆&#xff09; 内容&#xff1a;23考情概况&#xff08;拟录取和复试分析&#xff09;、院校概况、23专业目录、23复试详情、各专业考情分析、各科目考情分析。 正文1096字&#xff0c;预计阅读&#xff1a;3分钟 2023考情概况 北…

Gradio使用介绍

与他人分享你的机器学习模型、API或数据科学工作流的最佳方式之一&#xff0c;就是创建一个交互式应用程序&#xff0c;让用户或同事可以在他们的浏览器中尝试演示,Gradio是创建提供了用非常方便的方式快速创建一个前端交互应用&#xff0c;那如何使用Gradio呢&#xff1f;因为…

OLED透明屏显示技术:未来显示科技的领航者

OLED透明屏显示技术是一种创新性的显示技术&#xff0c;它的特殊性质使其成为未来显示科技的领航者。 OLED透明屏具有高对比度、快速响应时间、广视角和低功耗等优势&#xff0c;同时&#xff0c;其透明度、柔性和薄型设计使其成为创新设计的理想选择。 本文将深入探讨OLED透…

YII项目在Docker中运行缓慢

缓慢问题分析 请求YII的api时间请求原生查询时间win10 上运行docker上的php api异常慢ubuntu 中拉代码git报错 请求YII的api时间 请求原生查询时间 win10 上运行docker上的php api异常慢 链接阿里数据的 入口直接返回的 网上有说是docker的dns解析慢&#xff1b; 也有说是…

【报错记录】疯狂踩坑之RockyLinux创建Raid1镜像分区,Raid分区在重启后消失了!外加华硕主板使用Raid模式后,硬盘在系统中无法找到问题

前言 为了摆脱对于专业NAS的依赖&#xff0c;我决定专门使用一台Linux服务器安装NAS程序的方式实现NAS功能&#xff0c;这里就需要用到Raid功能&#xff0c;由于目前我只有3块SSD&#xff08;256G500G500G&#xff09;&#xff0c;在ChatGPT的推荐下还是使用一个256G系统盘2块…

等保测评各个级别的详细内容

等保测评是指信息系统安全等级保护测评&#xff0c;是我国信息安全领域中的一项重要工作。根据国家标准《信息系统安全等级保护基本要求》(GB/T 22239-2008)和《信息系统安全等级保护测评技术要求》(GB/T 25070-2010)。 等保测评分为五个级别&#xff0c;分别是&#xff1a;一级…

LLM推理部署(一):LLM七种推理服务框架总结

自从ChatGPT发布以来&#xff0c;国内外的开源大模型如雨后春笋般成长&#xff0c;但是对于很多企业和个人从头训练预训练模型不太现实&#xff0c;即使微调开源大模型也捉襟见肘&#xff0c;那么直接部署这些开源大模型服务于企业业务将会有很大的前景&#xff0c;本文将介绍七…

WebGPT VS WebGPU

推荐&#xff1a;使用 NSDT编辑器 快速搭建3D应用场景 随着WebGPU的引入&#xff0c;Web开发发生了有趣的转变&#xff0c;WebGPU是一种新的API&#xff0c;允许Web应用程序直接访问设备的图形处理单元&#xff08;GPU&#xff09;。这种发展意义重大&#xff0c;因为 GPU 擅长…

浅析token

上一章节我们学习了cookie和session机制&#xff0c;但是他们都有一些缺点&#xff0c;所有这次我们来了解一个机制---token。 一、cookie和session的缺点 cookie信息存储在客户端浏览器上&#xff0c;安全性较低&#xff0c;所以浏览器加入了一些限制确保cookie不会被恶意使用…

2023年京东婴童纸尿裤行业数据分析(京东数据运营)

当前&#xff0c;面对出生率下降、消费疲软等各种大环境不确定性&#xff0c;不仅是线下母婴店深陷于“生意难”的境地&#xff0c;线上消费同样受影响颇深&#xff0c;婴童纸尿裤类目便是如此。下面结合鲸参谋平台的数据&#xff0c;从行业大盘、品牌端等方面来看一下婴童纸尿…

Linux中的dpkg指令(dpkg -l | grep XXX等)

dpkg是Debian包管理系统中的一个工具&#xff0c;用于在Linux系统中安装、升级、删除和管理软件包。它是Debian、Ubuntu以及基于它们的发行版中的包管理器。 dpkg 有很多用法&#xff0c;常用之举例:dpkg -l | grep apt 显示系统中安装的与apt相关&#xff08;命名&#xff09…

什么是跨域(cross-origin)请求,如何解决跨域问题?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 跨域请求和跨域问题⭐ 解决跨域问题的方法1. CORS&#xff08;跨域资源共享&#xff09;2. JSONP&#xff08;JSON with Padding&#xff09;3. 代理服务器4. WebSocket5. 使用服务器中继 ⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff…

我是如何成为一名全栈工程师的?

经历了将近一年的时间&#xff0c;我终于阶段性地完成了从iOS开发到后端开发的角色转变。 现在我可以自豪地说&#xff0c;我已经接近一名全栈工程师了&#xff0c;已经熟悉了后端开发的各种工具、环境和一些后端工作的方式。 接下来&#xff0c;我将继续熟悉框架、工具、语言…

企业主流全链路监控系统 - 理论

企业主流全链路监控系统 1. 问题背景2. 目标要求1. 探针的性能消耗2. 代码的侵入性3. 可扩展性4.数据的分析 3. 功能模块1. 埋点与生成日志2. 收集和存储日志3. 分析和统计调用链路数据&#xff0c;以及时效性4. 展现以及决策支持 4. Google Dapper1. Span2. Trace3. Annotatio…