设计模式实战之观察者模式and模板方法模式

设计模式实战之观察者模式and策略模式

背景

本次实战的背景是工作流审批前需要对单据进行校验验证他是否可以提交。

思考

这很明显是一个全局性的问题,吧问题拆解成流程的生命周期。
校验发生在流程发起前,类似的,还可以设计流程发起后,以及流程过程中的例如退回前、退回后、等等,这些生命周期都可以提供统一的接口方法共业务类实现。其他类似这种也可以利用观察者模式进行解耦。
由原来的一条线写下来,变成发布一个生命周期事件由监听器监听并完成生命周期回调。利用模板方法模式封装统一行为,将类似验证这种个性化的操作使用抽象方法下发给具体的业务处理类。
就这次的需求而言这将会减少每次验证所需要写的大部分代码,,每次是需要实现自己想扩展的生命周期方法直接写业务逻辑。代码耦合度低冗余低且优雅。

问题拆分

将具体问题(需要进行流程发起的流程进行发起前校验)转化成 工作流生命周期回调。
发起前校验仅仅只是生命周期的一小部分,设计满足其他的生命周期可扩展。本次设计在符合开闭原则的条件下进行,不改变原有业务逻辑的运行。且类设计符合单一责任原则。

接口设计

模板方法类设计

设计了一个生命周期回调接口用于规范都有哪些生命周期方法。设计了AbstractProcessHelper类用于处理回调方法的统一处理比如校验传递的参数是否正确,处理异常等。这里在类中设计了抽象方法用于具体的逻辑实现类实现。设计了DefaultProcessFunImpl 作为具体的逻辑实现类,在逻辑实现类中调用业务类实现的具体逻辑。设计了ProcessFuncSupport 接口用于暴露回调函数给业务类。
在这里插入图片描述

观察者模式类设计

设计了ProcessAfterEvent和ProcessBeforeEvent事件,使用ApplicationContext上下文去发布生命周期事件,再设计ProcessListener去监听生命周期事件。调用生命周期回调。
在这里插入图片描述

具体实现

/**
 * 流程发布相关事件监听器
 *
 * @ClassName: ProcessAfterListener
 * @author: tpz
 * @date: 2023/4/17 20:55
 * @Version: V1.0
 */
@Component
public class ProcessListener {

    private static final Logger logger = LoggerFactory.getLogger(ProcessListener.class);

    @Resource
    private ProcessFunc defaultProcessFun;

    /**
     * @param processBeforeEvent
     * @Description: 流程发布前事件
     * @author: tpz
     * @date: 2023/4/18 9:17
     */
    @EventListener
    public void processBeforeListener(ProcessBeforeEvent processBeforeEvent) {
        logger.info("==流程发起前事件触发==");
        HashMap<String, Object> processVariable = (HashMap<String, Object>) processBeforeEvent.getSource();
        // 设置流程变量
        defaultProcessFun.addProcessVariable(processVariable);
        // 调用流程发起前验证
        defaultProcessFun.beforeStartCheck(oConvertUtils.getString(processVariable.get("id"),null),processVariable);
    }

    /**
     * @param processAfterEvent
     * @Description: 流程发布后事件
     * @author: tpz
     * @date: 2023/4/18 9:17
     */
    @EventListener
    public void processAfterListener(ProcessAfterEvent processAfterEvent) {
        logger.info("==流程发起后事件触发==");
        HashMap<String, Object> processVariable = (HashMap<String, Object>) processAfterEvent.getSource();
        // 设置流程变量
        defaultProcessFun.addProcessVariable(processVariable);
        // 调用流程发起后
        defaultProcessFun.afterStart(oConvertUtils.getString(processVariable.get("id"),null),processVariable);
    }

}
/**
 * 流程相关的全局性操作接口
 *
 * @ClassName: ProcessFunc
 * @author: tpz
 * @date: 2023/4/18 9:23
 * @Version: V1.0
 */
public interface ProcessFunc {


    /**
     * @Description: 流程发起前校验
     * @author: tpz
     * @Param: id 表单id
     * @date: 2023/4/18 9:25
     * @Return: 是否可以提交
     */
    ProcessCheckVo beforeStartCheck(String id, HashMap<String, Object> params);
    /**
     * @Description: 流程发起后校验
     * @author: tpz
     * @Param: id 表单id
     * @date: 2023年4月18日18:59:26
     * @Return: ProcessAfterVo
     */
    ProcessAfterVo afterStart(String id, HashMap<String, Object> params);

    /**
     * @Description: 设置线程私有参数
     * @author: tpz
     * @date: 2023/4/19 11:06
     *
     * @param value 参数
     */
    void addProcessVariable(HashMap<String, Object> value);

    /**
     * @Description: 获取线程私有参数
     * @author: tpz
     * @date: 2023/4/19 11:06
     *
     * @Return: 线程私有参数
     */
    public HashMap<String, Object> getProcessVariable();
}

/**
 * 封装流程相关的全局性操作
 *
 * @ClassName: AbstractProcessHelper
 * @author: tpz
 * @date: 2023/4/18 9:23
 * @Version: V1.0
 */
public abstract class AbstractProcessHelper implements ProcessFunc {

    private static final Logger logger = LoggerFactory.getLogger(DefaultProcessFunImpl.class);
    
    private final ThreadLocal<HashMap<String, Object>> threadLocal = new ThreadLocal<>();

    /**
     * @Description: 流程发起前校验模板方法,处理异常
     * @author: tpz
     * @date: 2023/4/18 9:30
     * @Return:
     */
    @Override
    public ProcessCheckVo beforeStartCheck(String id, HashMap<String, Object> params) {
        HashMap<String, Object> processVariable = getProcessVariable();
        ProcessCheckVo processCheckVo = new ProcessCheckVo();
        // 验证是否需要发起前验证
        if ("0".equals(oConvertUtils.getString(processVariable.get("isCheck"), "0"))) {
            processCheckVo.setIsChecked(Boolean.TRUE);
            return processCheckVo;
        }
        // 验证beanName和表单id是否存在 验证是否需要
        if (oConvertUtils.isEmpty(processVariable.get("id")) || oConvertUtils.isEmpty(processVariable.isEmpty())
                || oConvertUtils.isEmpty(processVariable.get("beanName"))) {
            logger.error("参数beanName或者表单id未传递!!!");
            throw new ProcessCheckException("参数beanName或者表单id未传递!!");
        } else {
            String beanName = (String) processVariable.get("beanName");
            beforeProcess(beanName, id, processVariable);
            processCheckVo.setIsChecked(Boolean.TRUE);
            return processCheckVo;
        }
    }

    @Override
    public ProcessAfterVo afterStart(String id, HashMap<String, Object> params) {
        HashMap<String, Object> processVariable = getProcessVariable();
        ProcessAfterVo processAfterVo = new ProcessAfterVo();
        // 验证是否需要发起前验证
        if ("0".equals(oConvertUtils.getString(processVariable.get("isAfterStart"), "0"))) {
            processAfterVo.setIsOk(Boolean.TRUE);
            return processAfterVo;
        }
        if (oConvertUtils.isEmpty(id)) {
            logger.error("表单id未传递!!!");
            throw new ProcessCheckException("表单id未传递!!");
        } else {
            String beanName = (String) processVariable.get("beanName");
            afterProcess(beanName, id, params);
            processAfterVo.setIsOk(Boolean.TRUE);
            return processAfterVo;
        }
    }

    /**
     * @param id 业务表单id
     * @param processVariable
     * @Description: 子类实现流程发起后回调
     * @author: tpz
     * @date: 2023/4/18 9:34
     */
    public abstract void afterProcess(String beanName, String id, HashMap<String, Object> processVariable);

    /**
     * @param id 业务表单id
     * @param processVariable
     * @Description: 子类实现流程前校验逻辑
     * @author: tpz
     * @date: 2023/4/18 9:34
     */
    public abstract void beforeProcess(String beanName, String id, HashMap<String, Object> processVariable);

    public void addProcessVariable(HashMap<String, Object> value) {
        threadLocal.set(value);
    }

    public HashMap<String, Object> getProcessVariable() {
        return threadLocal.get();
    }
}
/**
 * 流程相关功能的默认执行器
 *
 * @ClassName: DefaultProcessFunImpl
 * @author: tpz
 * @date: 2023/4/18 9:27
 * @Version: V1.0
 */
@Service
public class DefaultProcessFunImpl extends AbstractProcessHelper {

    private static final Logger logger = LoggerFactory.getLogger(DefaultProcessFunImpl.class);

    // 相关操作对应的Client
    @Resource
    private IProcessFunFeignClient processFeignClient;


    @Override
    public void afterProcess(String beanName, String id, HashMap<String, Object> processVariable) {
        AfterProcessDto afterProcessDto = new AfterProcessDto(id, beanName, processVariable);
        ProcessAfterVo processAfterVo = processFeignClient.afterProcess(afterProcessDto);
        Boolean isOk = processAfterVo.getIsOk();
        if (Boolean.TRUE.equals(isOk)) {
            logger.info("==流程发起通过后==");
        } else {
            logger.error("==流程发起通过后不通过==");
            Object data = processAfterVo.getData();
            // 可根据data取失败的描述
            throw new ProcessCheckException("流程验证错误");
        }
    }

    @Override
    public void beforeProcess(String beanName, String id, HashMap<String, Object> processVariable) {
        ProcessCheckDto processCheckDto = new ProcessCheckDto(id, beanName, processVariable);
        ProcessCheckVo processCheckVo = processFeignClient.beforeCheck(processCheckDto);
        Boolean isChecked = processCheckVo.getIsChecked();
        if (Boolean.TRUE.equals(isChecked)) {
            logger.info("==流程发起前验证通过==");
        } else {
            logger.error("==流程发起前验证不通过==");
            throw new ProcessCheckException(processCheckVo.getMsg());
        }
    }

}

    /**
     * @param beanName 校验类
     * @param dataId   表单id
     * @Description: 流程发起前校验
     * @author: tpz
     * @date: 2023/4/18 11:05
     * @Return: 表单验证信息
     */
    @Override
    public ProcessCheckVo beforeCheck(String beanName, String dataId, HashMap<String, Object> params) {
        ProcessCheckVo processCheckVo = new ProcessCheckVo();
        Object bean = SpringContextUtils.getBean(beanName);
        if (bean instanceof ProcessFuncSupport) {
            processCheckVo = ((ProcessFuncSupport) bean).beforeStartCheck(dataId, params);
        } else {
            // 如果验证的类没有实现流程生命周期支持类 则默认不验证
            processCheckVo.setIsChecked(Boolean.TRUE);
        }
        return processCheckVo;
    }
/**
 * 流程生命周期支持
 *
 * @ClassName: ProcessFuncSupport
 * @author: tpz
 * @date: 2023/4/18 11:11
 * @Version: V1.0
 */
public interface ProcessFuncSupport extends ProcessFunc {

    /**
     * @Description: 流程发起前校验的默认实现<br/>
     * 默认通过校验<br/>
     * @author: tpz
     * @date: 2023/4/19 9:41
     *
     * @param id 表单id
     * @param params 其他参数
     * @Return: ProcessCheckVo
     */
    @Override
    default ProcessCheckVo beforeStartCheck(String id, HashMap<String, Object> params) {
        ProcessCheckVo processCheckVo = new ProcessCheckVo();
        processCheckVo.setIsChecked(Boolean.TRUE);
        return processCheckVo;
    }

    /**
     * @Description: 流程发起后回调<br/>
     * 默认什么都不做<br/>
     * @author: tpz
     * @date: 2023/4/19 9:41
     *
     * @param id 表单id
     * @param params 其他参数
     * @Return: ProcessCheckVo
     */
    @Override
    default ProcessAfterVo afterStart(String id, HashMap<String, Object> params) {
        ProcessAfterVo processAfterVo = new ProcessAfterVo();
        processAfterVo.setIsOk(Boolean.TRUE);
        return processAfterVo;
    }

    // 这两个线程私有的流程参数没设计好 不该在这出现的 主要是跨服务包结构混乱 后期可提取重新设计重构
    @Override
    default void addProcessVariable(HashMap<String, Object> value){};

    @Override
    default HashMap<String, Object> getProcessVariable(){
        return null;
    };
}

需要使用生命周期回调的业务类只需要实现扩展接口ProcessFuncSupport重写具体的回调的默认方法即可。

总结

设计模式往往不是单个出现,大多是组合的形式出现,多观摩开源项目的源码会学习到很多优雅的代码思想。使用观察者模式可以很好的将流程性的功能拆分和解耦,使代码冗余降低耦合度降低,使用模板方法模式可以将统一的全局性代码封装,直将具体的个性化定制的业务放在抽象方法中给具体的业务代码实现,同样降低了业务代码的代码量和提高了业务代码的可维护性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值