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

一、需求介绍

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

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

二、设计模式选择

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

三、代码实现

1、准备工作

项目的目录结构如下:

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

  • 成功状态表示枚举类:
java 复制代码
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;
    }

}
  • 新建审批异常类:
java 复制代码
public class ApproveException extends RuntimeException {
    public ApproveException(String message){
        super(message);
    }
    public ApproveException(Throwable throwable){
        super(throwable);
    }
}
  • 新建常量类,表示不同审批结果,不同审批返回值需要处理不同的业务逻辑:
java 复制代码
public interface Constants {
    //不执行插入任务 与 执行任务的逻辑
    String FLOW_STATUS1 = "1";
    //只执行审批逻辑
    String FLOW_STATUS2 = "2";
    //不执行业务逻辑
    String FLOW_STATUS3 = "3";
    //执行所有逻辑
    String FLOW_STATUS4 = "4";
}
  • 封装审批参数类,用来规范审批值传递,通过这个类规定需要审批接口需要的参数:
java 复制代码
/**
 * @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()方法:这个是后来新加的添加任务后马上执行的需求,主要调用数据库的存储过程。

  • 审批服务接口:
java 复制代码
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()方法:执行审批,处理业务逻辑和审批逻辑之间的关系就是这个方法:
java 复制代码
public interface IApproveTemplate {

    String doApprove();

}
  • 模板类,通用模板逻辑封装,扩展需要在业务逻辑中实现的功能(抽象方法),实现了IApproveTemplate的doApprove()方法(钩子方法):
java 复制代码
/**
 * @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();

}
  • 业务逻辑抽象类,定义了用户需要在自己的业务服务类中所实现的功能,这些功能不同的业务是不一样的:
java 复制代码
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接口:
java 复制代码
/**
 * @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、业务逻辑服务实现

  • 定义服务接口,要继承封装的业务逻辑抽象类
java 复制代码
public interface IBusinessService extends CommonApproveService {
  //在这里可以写业务逻辑相关方法
}
  • 定义服务接口实现类
java 复制代码
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、通过动态代理对业务逻辑服务类进行增强,加上立刻执行存储过程的逻辑:

  • 代理类:
java 复制代码
/**
 * @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、测试

java 复制代码
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);
    }
}

执行结果:

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

相关推荐
马剑威(威哥爱编程)1 小时前
读写锁分离设计模式详解
java·设计模式·java-ee
修道-03231 小时前
【JAVA】二、设计模式之策略模式
java·设计模式·策略模式
G皮T4 小时前
【设计模式】结构型模式(四):组合模式、享元模式
java·设计模式·组合模式·享元模式·composite·flyweight
W_Meng_H4 小时前
设计模式-组合模式
设计模式·组合模式
吾与谁归in14 小时前
【C#设计模式(8)——过滤器模式(Adapter Pattern)】
设计模式·c#·过滤器模式
G皮T15 小时前
【设计模式】行为型模式(一):模板方法模式、观察者模式
java·观察者模式·设计模式·模板方法模式·template method·行为型模式·observer
iFlyCai18 小时前
23种设计模式的Flutter实现第一篇创建型模式(一)
flutter·设计模式·dart
zhouzhihao_0718 小时前
程序代码设计模式之模板方法模式(1)
java·设计模式·模板方法模式
xianwu54318 小时前
【设计模式】工厂模式
开发语言·c++·设计模式·简单工厂模式·抽象工厂模式
树懒_Zz1 天前
设计模式-状态模式(State)
设计模式·状态模式