【进阶玩法】策略+责任链+组合实现合同签章

1前置内容

  • 掌握策略模式
  • 掌握责任链模式
  • 掌握类继承、接口的实现
  • 掌握参数的传递与设置
  • GitHub地址

ps:【文章由来】 公司项目中所用的合同签章处理流程,本人基于责任链上使用策略模式进行优化。

2签章的处理流程

  • 合同文本初始化
  • 合同文本生成
  • 签章挡板是否开启
  • 合同签章发送mq
  • 合同签章流水更新
  • 合同上传文件服务器
  • 签章渠道选择
  • 签章渠道的实际调用

执行的流程如下:

整个结构类似于递归调用。每个节点中依赖上一个节点的输入以及下一个节点的输出,在中间过程可以实现每个节点的自定义操作,比较灵活。

3流程实现

GitHub地址

https://github.com/xbhog/DesignPatternsStudy

项目结构

DesignPatterns
└── src
    └── main
        └── java
            └── com.xbhog.chainresponsibility
                ├── annotations
                │    └── ContractSign
                ├── channel
                │    ├── ContractSignChannelImpl.java
                │    └── ContractSignChannel
                ├── Config
                │    └── SignConfig
                ├── Enum
                │    └── ContractSignEnum
                ├── impl
                │    ├── ContractSignCompactInitImpl.java
                │    ├── ContractSignGenerateImpl.java
                │    ├── ContractSignMockImpl.java  
                │    ├── ContractSignMqImpl.java
                │    ├── ContractSignSaveUploadImpl.java
                │    ├── ContractSignSerialImpl.java
                │    └── ContractSignTradeImpl.java
                ├── inter
                │    ├── Call
                │    ├── Chain
                │    ├── Interceptor
                │    └── Processor
                ├── pojo
                │    ├── ContractRequest.java
                │    └── ContractResponse.java
                ├── ContractCall
                ├── ContractChain
                └── ContractSignProcessor.java

项目类图

责任链+组合模式代码实现

工程结构
DesignPatterns
└── src
    └── main
        └── java
            └── com.xbhog.chainresponsibility
                ├── channel
                │    ├── ContractSignChannelImpl.java
                │    └── ContractSignChannel
                ├── impl
                │    ├── ContractSignCompactInitImpl.java
                │    ├── ContractSignGenerateImpl.java
                │    ├── ContractSignMockImpl.java  
                │    ├── ContractSignMqImpl.java
                │    ├── ContractSignSaveUploadImpl.java
                │    ├── ContractSignSerialImpl.java
                │    └── ContractSignTradeImpl.java
                ├── inter
                │    ├── Call
                │    ├── Chain
                │    ├── Interceptor
                │    └── Processor
                ├── pojo
                │    ├── ContractRequest.java
                │    └── ContractResponse.java
                ├── ContractCall
                ├── ContractChain
                └── ContractSignProcessor.java
责任链中的对象定义
//请求
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ContractRequest {

    private String name;

    private String age;

    private String status;
}
//响应
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ContractResponse {
    private String status;

    private String mas;
}

定义流程中的请求及响应类,方便处理每个责任链的请求、返回信息。

责任链处理流程
/**
 * @author xbhog
 * @describe: 责任链+组合实现合同签章
 * @date 2023/7/11
 */

@Slf4j
@Component
public class ContractSignProcessor <T extends ContractRequestimplements Processor<TContractResponse{

    @Resource(name = "contractSignCompactInitImpl")
    private Interceptor<T,ContractResponse> contractCompactInitImpl;
    ......


    public ContractSignProcessor() {
    }

    @Override
    public ContractResponse process(T paramter) {
        //获取所有的监听器
        List<Interceptor<T,ContractResponse>> interceptorList = new ArrayList<>();
        interceptorList.add(contractCompactInitImpl);
        ......
        //开始签章
        log.info("签章开始");
        return new ContractCall(paramter,interceptorList).exectue();
    }
}

合同签章方法的主流程调用接口(入口) ,该类中注入所有的节点实现类(如contractCompactInitImpl),通过编排实现责任链流程。

在初始化节点之前,进行节点的封装以及数据请求的处理。例:contractCompactInitImpl-合同数据初始化节点

/**
 * @author xbhog
 * @describe: 合同数据请求、节点的实例化及方法执行
 * @date 2023/7/11
 */

public class ContractCall<T extends ContractRequestimplements Call<TContractResponse{
    private final T originalRequest;
    private final List<Interceptor<T,ContractRequest>> interceptorList;

    public ContractCall(T originalRequest, List<Interceptor<T, ContractRequest>> interceptorList) {
        this.originalRequest = originalRequest;
        this.interceptorList = interceptorList;
    }

    @Override
    public T request() {
        return this.originalRequest;
    }

    @Override
    public ContractResponse exectue() {
        //实例化流程节点
        ContractChain<T> chain = new ContractChain(0,this.originalRequest,this.interceptorList);
        return chain.proceed(this.originalRequest);
    }
}

获取节点中的请求参数,实例化当前责任链节点(contractCompactInitImpl),在执行节点中的proceed方法来获取当前节点的参数以及获取节点的信息。

/**
 * @author xbhog
 * @describe: 合同节点
 * @date 2023/7/11
 */

@Slf4j
public class ContractChain<T extends ContractRequestimplements Chain<TContractResponse{
    private final Integer index;

    private final T request;

    private final List<Interceptor<T,ContractResponse>> interceptors;

    public ContractChain(Integer index, T request, List<Interceptor<T, ContractResponse>> interceptors) {
        this.index = index;
        this.request = request;
        this.interceptors = interceptors;
    }

    @Override
    public T request() {
        return this.request;
    }

    @Override
    public ContractResponse proceed(T request) {
        //控制节点流程
        if(this.index >= this.interceptors.size()){
            throw  new IllegalArgumentException("index越界");
        }
        //下一个节点参数设置
        Chain<T,ContractResponse> nextChain = new ContractChain(this.index + 1, request, this.interceptors);
        //获取节点信息
        Interceptor<T, ContractResponse> interceptor = this.interceptors.get(this.index);
        Class<? extends Interceptor> aClass = interceptor.getClass();
        log.info("当前节点:{}",aClass.getSimpleName());
        ContractResponse response = interceptor.process(nextChain);
        if(Objects.isNull(response)){
            throw new NullPointerException("intercetor"+interceptor+"return null");
        }
        return response;
    }
}

到此合同签章的架构流程已经确定,后续只要填充Interceptor具体的实现类即可。 在代码中ContractResponse response = interceptor.process(nextChain);来执行合同初始化节点的具体操作。

/**
 * @author xbhog
 * @describe: 合同文本初始化
 * @date 2023/7/12
 */

@Slf4j
@Component
public class ContractSignCompactInitImpl<T extends ContractRequestimplements Interceptor<TContractResponse{
    public ContractSignCompactInitImpl() {
    }

    @Override
    public ContractResponse process(Chain<T,ContractResponse> chain) {
        log.info("=============执行合同文本初始化拦截器开始");
        //获取处理的请求参数
        T request = chain.request();
        request.setStatus("1");
        log.info("=============执行合同文本初始化拦截器结束");
        //进入下一个责任链节点
        ContractResponse response =  chain.proceed(request);
        if(Objects.isNull(response)){
            log.error("返回值的为空");
            response = ContractResponse.builder().status("fail").mas("处理失败").build();
        }
        //其他处理
        return response;
    }
}
测试验证
@SpringBootTest
class SPringBootTestApplicationTests {
    @Autowired
    @Qualifier("contractSignProcessor")
    private Processor<ContractRequest,ContractResponse> contractSignProcessor;

    @Test
    void contextLoads() {
        ContractRequest contractRequest = new ContractRequest();
        contractRequest.setName("xbhog");
        contractRequest.setAge("12");
        ContractResponse process = contractSignProcessor.process(contractRequest);
        System.out.println(process);
    }

}

在这里只需要调用合同签章入口的方法即可进入合同签章的流程。

2023-07-16 13:25:13.063  INFO 26892 --- [           main] c.e.s.c.ContractSignProcessor            : 签章开始
2023-07-16 13:25:13.067  INFO 26892 --- [           main] c.e.s.chainresponsibility.ContractChain  : 当前节点:ContractSignCompactInitImpl
2023-07-16 13:25:13.068  INFO 26892 --- [           main] c.e.s.c.i.ContractSignCompactInitImpl    : =============执行合同文本初始化拦截器开始
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.i.ContractSignCompactInitImpl    : =============执行合同文本初始化拦截器结束
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.chainresponsibility.ContractChain  : 当前节点:ContractSignGenerateImpl
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignGenerateImpl    : =============执行合同文本生成拦截器开始
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignGenerateImpl    : =============执行合同文本生成拦截器结束
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.chainresponsibility.ContractChain  : 当前节点:ContractSignMockImpl
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignMockImpl        : =============执行签章挡板拦截器开始
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignMockImpl        : =============执行签章挡板拦截器结束
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.chainresponsibility.ContractChain  : 当前节点:ContractSignMqImpl
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignMqImpl          : =============执行合同签章完成发送mq拦截器开始
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.chainresponsibility.ContractChain  : 当前节点:ContractSignSerialImpl
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignSerialImpl      : =============执行合同签章流水处理拦截器开始
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.chainresponsibility.ContractChain  : 当前节点:ContractSignSaveUploadImpl
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignSaveUploadImpl  : =============执行合同签章完成上传服务器拦截器开始
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.chainresponsibility.ContractChain  : 当前节点:ContractSignTradeImpl
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignTradeImpl       : =============执行签章渠道实际调用拦截器开始
2023-07-16 13:25:13.069  INFO 26892 --- [           main] c.e.s.c.channel.ContractSignChannelImpl  : 签章处理开始
2023-07-16 13:25:13.070  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignSaveUploadImpl  : 开始上传服务器
2023-07-16 13:25:13.070  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignSaveUploadImpl  : .............
2023-07-16 13:25:13.070  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignSaveUploadImpl  : 上传服务器完成
2023-07-16 13:25:13.070  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignSaveUploadImpl  : =============执行合同签章完成上传服务器拦截器结束
2023-07-16 13:25:13.070  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignSerialImpl      : =============执行合同签章流水处理拦截器结束
2023-07-16 13:25:13.070  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignMqImpl          : 发送MQ给下游处理数据
2023-07-16 13:25:13.070  INFO 26892 --- [           main] c.e.s.c.impl.ContractSignMqImpl          : =============执行合同签章完成发送mq拦截器结束
ContractResponse(status=success, mas=处理成功)

策略+责任链+组合代码实现

以下是完整的合同签章入口实现类:

/**
 * @author xbhog
 * @describe: 责任链+组合实现合同签章
 * @date 2023/7/11
 */

@Slf4j
@Component
public class ContractSignProcessor <T extends ContractRequestimplements Processor<TContractResponse{

    @Resource(name = "contractSignCompactInitImpl")
    private Interceptor<T,ContractResponse> contractCompactInitImpl;

    @Resource(name = "contractSignGenerateImpl")
    private Interceptor<T,ContractResponse> contractGenerateImpl;

    @Resource(name = "contractSignMockImpl")
    private Interceptor<T,ContractResponse> contractSignMockImpl;

    @Resource(name = "contractSignMqImpl")
    private Interceptor<T,ContractResponse> contractSignMqImpl;

    @Resource(name = "contractSignSaveUploadImpl")
    private Interceptor<T,ContractResponse> contractSignSaveUploadImpl;

    @Resource(name = "contractSignSerialImpl")
    private Interceptor<T,ContractResponse> contractSignSerialImpl;

    @Resource(name = "contractSignTradeImpl")
    private Interceptor<T,ContractResponse> ContractSignTradeImpl;


    public ContractSignProcessor() {
    }

    @Override
    public ContractResponse process(T paramter) {
        //获取所有的监听器
        List<Interceptor<T,ContractResponse>> interceptorList = new ArrayList<>();
        interceptorList.add(contractCompactInitImpl);
        interceptorList.add(contractGenerateImpl);
        interceptorList.add(contractSignMockImpl);
        interceptorList.add(contractSignMqImpl);
        interceptorList.add(contractSignSerialImpl);
        interceptorList.add(contractSignSaveUploadImpl);
        interceptorList.add(ContractSignTradeImpl);
        //开始签章
        log.info("签章开始");
        return new ContractCall(paramter,interceptorList).exectue();
    }
}

可以看到,目前的合同签章的处理流程需要的节点数已经7个了,后续如果新增节点或者减少节点都需要对该类进行手动的处理;比如:减少一个节点的流程。

  • 删除节点实现的注入
  • 删除list中的bean实现类

为方便后续的拓展(懒是社会进步的加速器,不是),在责任链,组合的基础上通过策略模式来修改bean的注入方式。 完整的项目结构和项目类图就是作者文章开始放的,可返回查看。 在第一部分的基础上增加的功能点如下

  • 新增签章注解
  • 新增签章节点枚举
  • 新增签章配置类
签章注解实现
package com.example.springboottest.chainresponsibility.annotations;

import com.example.springboottest.chainresponsibility.Enum.ContractSignEnum;

import java.lang.annotation.*;

/**
 * @author xbhog
 * @describe:
 * @date 2023/7/15
 */

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContractSign {
    ContractSignEnum.SignChannel SIGN_CHANNEL();

}

设置注解修饰对象的范围,主要是对bean的一个注入,所以类型选择type,

  • TYPE: 用于描述类、接口(包括注解类型) 或enum声明

设置注解的运行周期(有效范围),一般是运行时有效,

  • RUNTIME:在运行时有效 (大部分注解的选择)

设置该注解的数据类型,

  • ENUM:枚举类型,方便统一处理
枚举实现
package com.xbhog.chainresponsibility.Enum;

/**
 * @author xbhog
 * @describe:
 * @date 2023/7/15
 */

public class ContractSignEnum {
    public enum SignChannel {

        SIGN_INIT(1"合同文本初始化"),
        SIGN_GENERATE(2"合同文本生成"),
        SIGN_MOCK(3"签章挡板"),
        SIGN_MQ(4"合同签章完成发送MQ"),
        SIGN_TABLE(5"合同签章表处理"),
        SIGN_UPLOAD(6"合同签章完成上传服务器"),
        SIGN_TRADE(7"签章渠道实际调用");

        private Integer code;
        private String info;

        SignChannel(Integer code, String info) {
            this.code = code;
            this.info = info;
        }
        ......
    }
}

对合同签章中的流程节点进行统一的配置。

签章配置类

在项目启动的时候,通过注解工具类AnnotationUtils扫描所有被ContractSign注解修饰的类,将这些类通过Map进行存储,方便后续的调用。

public class SignConfig {
    @Resource
    protected List<Interceptor> contractSignList;

    protected static final Map<Integer,Interceptor> CONTRACT_SIGN_MAP = new ConcurrentHashMap<>();

    @PostConstruct
    public void init(){
       contractSignList.forEach(interceptor -> {
           //查找这个接口的实现类上有没有ContractSign注解
           ContractSign sign = AnnotationUtils.findAnnotation(interceptor.getClass(), ContractSign.class);
           if(!Objects.isNull(sign)){
               CONTRACT_SIGN_MAP.put(sign.SIGN_CHANNEL().getCode(),interceptor);
           }
       });
    }

}

到此,简化了Bean的注入方式。

签章注解使用

以合同文本初始化ContractSignCompactInitImpl来说。

/**
 * @author xbhog
 * @describe: 合同文本初始化
 * @date 2023/7/12
 */

@Slf4j
@ContractSign(SIGN_CHANNEL = ContractSignEnum.SignChannel.SIGN_INIT)
@Component
public class ContractSignCompactInitImpl<T extends ContractRequestimplements Interceptor<TContractResponse{
    public ContractSignCompactInitImpl() {
    }

    @Override
    public ContractResponse process(Chain<T,ContractResponse> chain) {
        log.info("=============执行合同文本初始化拦截器开始");
        //获取处理的请求参数
        T request = chain.request();
        request.setStatus("1");
        log.info("=============执行合同文本初始化拦截器结束");
        //进入下一个责任链节点
        ContractResponse response =  chain.proceed(request);
        if(Objects.isNull(response)){
            log.error("返回值的为空");
            response = ContractResponse.builder().status("fail").mas("处理失败").build();
        }
        //其他处理
        return response;
    }
}

在该实现类上绑定了枚举@ContractSign(SIGN_CHANNEL = ContractSignEnum.SignChannel.SIGN_INIT). 在合同签章入口类( ContractSignProcessor )中的变更如下:

@Slf4j
@Component
public class ContractSignProcessor <T extends ContractRequestextends SignConfig implements Processor<TContractResponse{

    public ContractSignProcessor() {
    }

    @Override
    public ContractResponse process(T paramter) {
        //获取所有的监听器
        List<Interceptor<T,ContractResponse>> interceptorList = new ArrayList<>();
        //获取排序后的结果,保证责任链的顺序,hashmap中key如果是数字的话,通过hashcode编码后是有序的
        for(Integer key : CONTRACT_SIGN_MAP.keySet()){
            interceptorList.add(CONTRACT_SIGN_MAP.get(key));
        }
        //开始签章
        log.info("签章开始");
        return new ContractCall(paramter,interceptorList).exectue();
    }
}

通过继承合同签章配置类(SignConfig),来获取Map,遍历Map添加到list后进入责任链流程。 到此,整个策略+责任链+组合的优化方式结束了。

问题:责任链中的顺序是怎么保证的? 相信认真看完的你能在文章或者代码中找到答案。

来源:juejin.cn/post/7256307512244650040
后端专属技术群

构建高质量的技术交流社群,欢迎从事编程开发、技术招聘HR进群,也欢迎大家分享自己公司的内推信息,相互帮助,一起进步!

文明发言,以交流技术职位内推行业探讨为主

广告人士勿入,切勿轻信私聊,防止被骗

加我好友,拉你进群

相关推荐

  • 一个Demo搞定前后端大文件分片上传、断点续传、秒传
  • 重磅!西工大李学龙团队研发大模型自主无人机集群!
  • 8年干出千亿市值,东南亚最大快递IPO了
  • 李克强:大力发展新一代信息技术、人工智能、数字经济等(2020 年 1 月)
  • 再看大模型微调数据质量如何评估:已有方法回顾及IFD指令遵循难度筛选的思路与聚类细节
  • Python制作进度条,原来有这么多方法
  • 顶级ML后端工程师“进化”指南
  • Instagram 仅 3 名工程师,如何做到 1400 万用户?
  • 第五要素|大模型时代,数据治理与AI相互依存形成闭环
  • 纵腾湖仓全链路落地实践
  • Midjourney重大升级,网页版正式上线!生成图像真实清晰
  • 港大等发布GraphGPT:1/50微调参数,准确率提升10倍!无需超长token,LLM也能读懂图结构
  • 陶哲轩论文漏洞竟被AI发现,26年预言要成真!看定理名猜出研究方向,大神直呼AI能力惊人
  • 中美程序员不完全对比,太真实了。。。
  • 生成的分子几乎 100% 有效,用于逆向分子设计的引导扩散模型
  • 小模型如何比肩大模型,北理工发布明德大模型MindLLM,小模型潜力巨大
  • 多模态搜索算法如何让视频搜索更精准?腾讯独家揭秘,超详细
  • 谷歌20亿美元砸向Anthropic:大模型军备竞赛升级
  • 国内唯一全面对标OpenAI的创业公司,大模型已经出到第三代
  • 阿里语雀突发P0级事故,一度崩溃8小时!故障原因和补偿来了。