利用设计模式优化项目实际的申报业务

业务场景

普通的申报审批流程,申报提交,后由其他人进行决定通过,不通过,退回等操作,不同业务执行不同的程序,有的还会跨服务去调取其他系统等等,

问题

之前的老项目实在是难以看,到处是if-else,代码十分冗余,写到一个service导致后期业务越来越繁多时变得难以阅读,上面注入的mapper和servcie可达十几种。

现在新项目也有类似功能,因为这种套路以后基本上也是大差不差的,这里记录一下刚刚测试的思路,感觉这种模式可以应对大部分业务情况。

 

流程

前台或者其他系统调取接口,传来需要查询的业务类型(比如A31,A38这种)和申报的ID,并且携带一个ID,指定这是哪个库,哪个业务下的ID等等

 

大致结构

approval是审批的接口,就是有多少个业务就有多少种实现接口,A31这是数据库名称

entity是实体类,后台根据数据库创建一些对应的实体类

mapper是相关myabtis-plus查询接口

report是申报,由于不同表或者别的系统里的业务的字段名都不一样,统一用接口管理方法

service是代替之前写了大量业务流程的服务

利用设计模式优化项目实际的申报业务_第1张图片

代码

1.审批

首先是关于审批的部分,所有审批都有的功能:提交某个申报,通过某个申报,拒绝某个申报,退回某个申报,根据面对接口编程思想(开放-关闭原则),创建Approval接口

考虑到以后要判断携带的code状态来判断执行哪个流程,可以再创建一个申报接口,再实现四种申报实现类,但这样做又不合理,申报种类十几种,管理起来也会费劲,这里干脆直接通过模板方法统一判断执行就可以。

所以修改Approval为抽象类,通过public和抽象类的默认 protected,对外只需暴露出execute这一个方法去执行即可,其他内部方法无需给外界展示。

@Data
public abstract class Approval {

    public void execute(Report report){
        switch (report.getCode()){
            case "1":
                pass(report);
            break;
            case "2":
                noPass(report);
            break;
            case "3":
                back(report);
            break;
            case "4":
                submit(report);
            break;
        }
    }
    abstract void pass(Report report);
    abstract void noPass(Report report);
    abstract void back(Report report);
    abstract void submit(Report report);
}

 A31申报的实现接口,这里加入一个SysUserMapper是随意加的,意味着不同业务他注入的mapper、service也是不同的,然后根据自己的情况调用服务执行业务即可。

@Component
public class A31Approval extends Approval {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Override
    void pass(Report report) {
        System.out.println("A31通过了报告");
    }

    @Override
    void noPass(Report report) {
        System.out.println("A31拒绝通过了报告");
    }

    @Override
    void back(Report report) {
        System.out.println("A31退回了报告");
    }

    @Override
    void submit(Report report) {
        System.out.println("A31提交了报告");
    }
}

 同理这是另外的A38业务的申报,里面注入了不同的服务,用两个来对比。

@Component
public class A38Approval extends Approval {

    private A31Mapper a31Mapper;

    private A38Mapper a38Mapper;

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private FileBaseMapper fileBaseMapper;

    @Override
    void pass(Report report) {
        System.out.println("A38通过了报告");
    }

    @Override
    void noPass(Report report) {
        System.out.println("A38拒绝通过了报告");
    }

    @Override
    void back(Report report) {
        System.out.println("A38退回了报告");
    }

    @Override
    void submit(Report report) {
        System.out.println("A38提交了报告");
    }
}

2.申报类

因为不仅本系统的申报,还有其他系统里的申报有时候也需要进行处理,不同数据库,不通字段,多的让人分不清哪个是哪个,干脆统一继承Report接口,统一都用接口的方法返回值,下次再取值的时候就不会分不清字段了,当然这里还能更加细分,现在只是随意举例出几个熟悉。


public interface Report {
    String getTableName();
    String getId();
    String getTypeId();
    String getTypeDetailId();
    String getCode();
}

既然只是前置组织数据,分开写到每个service里不太合适吧,因为前台跟服务器是同一调用一个接口来返回数据的,都需要根据传来的参数进行数据查询,组织好对象后进行进一步判断,这里就创建一个工厂类同一组织数据,注意,这里距离只是返回一个Report声明,但是实际情况可能是很大不同,比如有的可能需要额外的List数据,这个时候只需要修改Report的继承类,添加新的属性,对外同一返回接口声明,不影响其他业务。

@Component
public class ReportFactory {

    private A31Mapper a31Mapper;

    private A38Mapper a38Mapper;


    public Report getReport(AccessEntity accessEntity){
        String tableName = accessEntity.getTableName();
        String id = accessEntity.getId();
        switch (tableName){
            case "A38":
                //A38 a38 = a38Mapper.selectById(id);
                A38 a38 = new A38();
                a38.setId("1");
                a38.setA3804("a38_A3804");
                a38.setA3808("a38_a3808");
                a38.setCode(accessEntity.getCode());
                return a38;
            case "A31":
                //A31 a31 = a31Mapper.selectById(id);
                A31 a31 = new A31();
                a31.setId("2");
                a31.setA3104("a31_A3104");
                a31.setA3108("a31_a3108");
                a31.setCode(accessEntity.getCode());
                return a31;
            default:
                return null;
        }
    }

}

3.实体类

根据数据库表创建不同的实体对象,没什么可说的,这里直接继承Report接口,实现接口的对象,不管是本数据库还是其他系统的对象,均通过Report里返回:业务上相同,命名上不同的属性。

package com.test.restructure.entity;

import com.test.restructure.report.Report;
import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;

@Data
public class A38 implements Serializable, Report {
    private String code;

    private String id;

    private String orgId;

    private BigDecimal rState;

    private BigDecimal a01State;

    public BigDecimal getA01State() {
        return a01State;
    }

    public void setA01State(BigDecimal a01State) {
        this.a01State = a01State;
    }

    private BigDecimal sSort;

    private BigDecimal aState;

    private Date aTime;

    private Date createtime;

    private String operator;

    private Date updatetime;

    private String updater;

    private String a3801;

    private String a3802;

    private Date a3803;

    private String a3804;

    private String a3805;

    private String a3806;

    private String a3807;

    private String a3809;

    private String a3808;

    private String a3899;

    private String a3803String;

    private static final long serialVersionUID = 1L;


    @Override
    public String getTableName() {
        return "A38";
    }

    @Override
    public String getTypeId() {
        return a3804;
    }

    @Override
    public String getTypeDetailId() {
        return a3808;
    }

}

 同理,创建另外一个用来对比

@Data
public class A31 implements Serializable, Report {
    private String code;

    private String id;

    private String orgId;

    private String m01Id;

    private String a3101;

    private String a3102;

    private Date a3103;

    private String a3104;

    private String a3105;

    private String a3106;

    private String a3107;

    private BigDecimal rState;

    private BigDecimal rSort;

    private BigDecimal aState;

    private Date aTime;

    private Date createtime;

    private String operator;

    private Date updatetime;

    private String updater;

    private String a3109;

    private String a3108;

    private String a3199;

    private static final long serialVersionUID = 1L;

    @Override
    public String getTableName() {
        return "A31";
    }

    @Override
    public String getTypeId() {
        return a3104;
    }

    @Override
    public String getTypeDetailId() {
        return a3108;
    }
}

这个是用来接收数据的,没别的意义

package com.test.restructure.entity;

import lombok.Data;

@Data
public class AccessEntity {
    private String id;
    private String tableName;
    private String code;
}

4.流程

如果是后台rpc服务,换一些注解就行了,反正都是接收数据用的。

@RestController
@RequestMapping("/controller")
public class TestController {
    @Autowired
    private ApprovalExecuteService approvalExecuteService;

    @RequestMapping("/approval")
    public void approval(AccessEntity accessEntity){
        approvalExecuteService.execute(accessEntity);
    }
}

在这个 ApprovalExecuteService 统一对外接收数据,统一管理各个审批单例,不关心每个业务的内部情况,对外只暴露出execute方法,若有新的业务出现只需要再添加Approval实现,修改也只需要修改每个Approval实现类里的各自功能,方便管理。这里直接用swtich返回

根据常见的策略模式,这里就是根据情况new的,例子没考虑到实际情况肯定不是new的也得是从容器里用原型复制出来的吧,但是这样似乎就是多例了,我虽然不了解原型模式效率如何,但是应该也会继承动态注入组建的,这样直接判断执行其实也OK吧。

@Component
public class ApprovalExecuteService {

    @Autowired
    private A31Approval a31Approval;

    @Autowired
    private A38Approval a38Approval;

    @Autowired
    private ReportFactory reportFactory;

    public void execute(AccessEntity accessEntity){
        Report report = reportFactory.getReport(accessEntity);
        executeApproval(report);
    }

    void executeApproval(Report report){
        switch (report.getTableName()){
            case "A38":
                a38Approval.execute(report);
                break;
            case "A31":
                a31Approval.execute(report);
                break;
            default:
                break;
        }
    }

}

测试

打开浏览器,输入:http://localhost:8080/controller/approval?tableName=A31&code=2

结果打印:A31拒绝通过了报告

输入:http://localhost:8080/controller/approval?tableName=A38&code=3

结果打印:A38退回了报告

符合预期目标,以后按照此套路可以优化很多类似场景,只需要对业务进行抽象归纳。

你可能感兴趣的:(java编程基础,java)