业务上第一次使用多个设计模式的组合(策略模式+缺省适配器模式+模板方法模式+简单工厂模式)

前言:

从第一次系统地阅读《Java与模式》这本书已经过去一年多了,我一直没有有意的去使用设计模式,原因有两点:一是,对设计模式理解有些点不深入,使用起来比较吃力;二是,没有一个合适场景去使用设计模式。最近,我在负责一个新系统的对接钉钉流程中心,调用相关流程的开发工作,我在之前已经钉钉流程对接2次了,都这块业务理解比较准确和成熟。如果要是搬用原来的代码没有问题,但是原来的写法复用很差,每次新增一个流程,都有把原来的接口复制一遍,然后再对每一个方法中部分代码进行修改。基于以上背景,我觉得应该使用设计模式,对钉钉流程模块进行重新架构。

一、业务简单介绍:

对钉钉流程的业务进行抽象,其实也没有几个操作,主要操作如下:发起流程操作(首次发起、重新发起)、处理实例同意、处理实例拒绝、处理实例撤回、处理各个任务等,操作就是方法,接口里面就会有这几个方法。当然了某些流程可能会有些其它操作,即其它的方法。

Tips:实例:调用一次钉钉流程称为一个实例;任务:钉钉流程中有各个审批节点,每个节点称为一个任务。

二、设计模式的应用:

1.设计的思想:

通过以上对业务的简单介绍,最笨的实现方法就是写一个接口,接口中有这几个方法,然后实现接口中方法,下次来一个流程复制原来代码进行修改,然后以此类推,好一点程序员会对每个方法公用的部分抽象成私有方法,让每个方法调用。 其实这样很笨,代码复用很低,不方便维护。其实,这里可以使用策略模式,每个流程都有这几个操作,每种具体流程都是一种策略,即抽象成代码语言就是,有一个公用流程接口,每一种流程去实现这个接口;但是单独使用一个策略模式是不行的,因为每一种流程中的操作都有重复的子操作,例如,处理实例相关操作都会保存审批记录。这些操作中的重复子操作可以运用模板方法的设计模式,即顶层帮你实现公共通用的操作,你只需要基础该顶层抽象类。到现在,你会遇到一个问题,如何让策略模式的接口,可以使用上模板方法模式,这里就需要缺省适配器模式进行过度。等这些操作完成后,你会发现策略模式的调用很不方便,这里也可使用简单工厂方法模式,让工厂来创建具体的策略。

我在重新架构钉钉流程模块的时候,也使用Spring bean周期的中一些思想(可能其他方法中也有),Spring bean会提供初始化bean之前 操作的方法和初始化bean成功之后的操作方法,方便操作者对bean的修改。这个发起流程的操作有点想,调用流程中心的操作我会封装到底层,调用钉钉流程之前,需要一个方法封装钉钉相关组件;调用钉钉流程成功之后,需要一个方法去处理其他的业务操作。策略接口中也使用到了泛型,抽象方法中也使用了断言,这里就不展开介绍了。

学习设计模式,一定少不了UML图,下面的类图(使用visual-paradigm在线版工具画出的类图)就是对这次钉钉流程重新架构的描述,因为是内部系统, 不方便使用源代码,我这里就模拟了两个方法(发起流程和处理实例同意)。

2.相关代码:

(1)策略接口:

package com.hanxiaozhang.designpattern.strategy.service;

/**
 * 〈一句话功能简述〉<br>
 * 〈策略模式接口〉
 *
 * @author hanxinghua
 * @create 2020/5/11
 * @since 1.0.0
 */
public interface ProcessStrategy<T> {


    /**
     * 发起流程
     *
     * @param t
     * @param againFlag 重新发起标识:true 重新发起,false 第一次发起
     */
    void startProcess(T t, Boolean againFlag);


    /**
     * 处理实例同意
     *
     * @param instance
     */
    void handleInstanceAgree(Object instance);



}

 (2)策略抽象的缺省适配器类:

package com.hanxiaozhang.designpattern.strategy.service.impl;

import com.hanxiaozhang.common.dao.DictDao;
import com.hanxiaozhang.common.domain.DictDO;
import com.hanxiaozhang.designpattern.strategy.service.ProcessStrategy;
import org.springframework.transaction.annotation.Transactional;


import javax.annotation.Resource;

/**
 * 〈一句话功能简述〉<br>
 * 〈抽象策略的缺省适配器〉
 *
 * @author hanxinghua
 * @create 2020/5/11
 * @since 1.0.0
 */
public abstract class AbstractProcessStrategyAdapter<T> implements ProcessStrategy<T> {

    @Resource
    private DictDao dictDao;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public  void  startProcess(T t,Boolean againFlag){

        // 发起流程之前的操作
        Object obj = this.startProcessOperateBefore(t,againFlag);

        // 断言
        assert obj == null:"[startProcessOperateBefore]方法返回值空,调用流程会报错!";

        // 调用流程中心
        System.out.println("模拟调用流程中心..."+obj.toString());

        // 发起流程之后的操作
        this.startProcessOperateAfter(t,againFlag);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public  void  handleInstanceAgree(Object instance) {

        // 模拟保存
        this.saveAgreeApprovalLog(instance);

        // 实例同意的操作
        this.instanceAgreeOperate(instance);

    }


    /**
     * 发起流程之前的操作
     *
     * @param t
     * @param againFlag
     * @return
     */
    public abstract Object startProcessOperateBefore(T t,Boolean againFlag);


    /**
     * 发起流程成功之后的操作
     *
     * @param t
     * @param againFlag
     */
    public abstract void startProcessOperateAfter(T t,Boolean againFlag);


    /**
     * 实例同意的操作
     *
     * @param instance
     */
    public abstract void instanceAgreeOperate(Object instance);


    /**
     * 保存实例同意的审批记录
     *
     * @param instance
     */
    private void saveAgreeApprovalLog(Object instance){

        DictDO dict = DictDO.builder().name("测试一下").build();
        dictDao.save(dict);

//        throw  new RuntimeException("模拟一下异常");
    }


}

(3)流程A策略类: 

package com.hanxiaozhang.designpattern.strategy.service.impl;

import com.hanxiaozhang.designpattern.strategy.domain.A;
import org.springframework.stereotype.Service;

/**
 * 〈一句话功能简述〉<br>
 * 〈〉
 *  建议强制指定一下bean的id
 *
 * @author hanxinghua
 * @create 2020/5/11
 * @since 1.0.0
 */
@Service("aProcess")
public class AProcess extends AbstractProcessStrategyAdapter<A> {

    @Override
    public Object startProcessOperateBefore(A a, Boolean againFlag) {

        System.out.println("流程A发起流程前的准备...");

        return new Object();
    }

    @Override
    public void startProcessOperateAfter(A a, Boolean againFlag) {

        System.out.println("流程A发起流程成功后的处理...");

    }

    @Override
    public void instanceAgreeOperate(Object instance) {

        System.out.println("流程A同意的处理...");

    }


}

(4)流程B策略类:  

package com.hanxiaozhang.designpattern.strategy.service.impl;

import com.hanxiaozhang.designpattern.strategy.domain.B;
import org.springframework.stereotype.Service;

/**
 * 〈一句话功能简述〉<br>
 * 〈〉
 *
 * @author hanxinghua
 * @create 2020/5/11
 * @since 1.0.0
 */
@Service("bProcess")
public class BProcess extends AbstractProcessStrategyAdapter<B> {


    @Override
    public Object startProcessOperateBefore(B b, Boolean againFlag) {

        System.out.println("流程B发起流程前的准备...");

        return new Object();
    }

    @Override
    public void startProcessOperateAfter(B b, Boolean againFlag) {

        System.out.println("流程B发起流程成功后的处理...");

    }

    @Override
    public void instanceAgreeOperate(Object instance) {

        System.out.println("流程B同意的处理...");

    }
}

(5)策略工厂类:   

package com.hanxiaozhang.designpattern.strategy.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * 〈一句话功能简述〉<br>
 * 〈策略工厂〉
 *
 * @author hanxinghua
 * @create 2020/5/11
 * @since 1.0.0
 */
@Component
public class ProcessStrategyFactory {

    /**
     * Spring会自动将Strategy接口的实现类注入到这个Map中,
     * key为bean id,value值则为对应的策略实现类
     *
     */
    @Autowired
    private Map<String, ProcessStrategy> strategyMap;


    /**
     * 通过名字获取策略实现类
     *
     * @param strategyName
     * @return
     */
    public ProcessStrategy getByName(String strategyName) {
        return strategyMap.get(strategyName);
    }


}

(6)调用测试类:    

package com.hanxiaozhang.designpattern.strategy.controller;

import com.hanxiaozhang.designpattern.strategy.constant.ProcessTypeEnum;
import com.hanxiaozhang.designpattern.strategy.domain.A;
import com.hanxiaozhang.designpattern.strategy.domain.B;
import com.hanxiaozhang.designpattern.strategy.service.ProcessStrategyFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 〈一句话功能简述〉<br>
 * 〈测试Controller〉
 *
 * @author hanxinghua
 * @create 2020/5/11
 * @since 1.0.0
 */
@Controller
@RequestMapping("/")
public class TestController {


    @Autowired
    private ProcessStrategyFactory strategyFactory;

    @RequestMapping("")
    @ResponseBody
    public String test(){
        strategyFactory.getByName(ProcessTypeEnum.A.getKey()).startProcess(new A(),false);
        strategyFactory.getByName(ProcessTypeEnum.B.getKey()).startProcess(new B(),false);
        return "OK";
    }


}

 (7)其他:    

package com.hanxiaozhang.designpattern.strategy.constant;

/**
 * 〈一句话功能简述〉<br>
 * 〈Process流程枚举类〉
 *
 * @author hanxinghua
 * @create 2020/5/14
 * @since 1.0.0
 */
public enum ProcessTypeEnum {

    A("aProcess","流程A"),
    B("bProcess","流程B");

    private String key;
    private String name;

    ProcessTypeEnum(String key, String name) {
        this.key = key;
        this.name = name;
    }

    public String getKey() {
        return key;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "ProcessTypeEnum{" +
                "key='" + key + '\'' +
                ", name='" + name + '\'' +
                '}';
    }

}

-------------------------------------------------------------
package com.hanxiaozhang.designpattern.strategy.domain;

/**
 * 〈一句话功能简述〉<br>
 * 〈流程A实例对象〉
 *
 * @author hanxinghua
 * @create 2020/5/14
 * @since 1.0.0
 */
public class A {

}
-------------------------------------------------------------
package com.hanxiaozhang.designpattern.strategy.domain;

/**
 * 〈一句话功能简述〉<br>
 * 〈流程B实例对象〉
 *
 * @author hanxinghua
 * @create 2020/5/14
 * @since 1.0.0
 */
public class B {

}


 三、源码地址:

https://gitee.com/hanxinghua2017/springboot_demo.git 《 springboot-spring-knowledge模块-designpattern.strategy包》

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hanxiaozhang2018

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值