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

业务场景

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

问题

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

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

 

流程

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

 

大致结构

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

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

mapper是相关myabtis-plus查询接口

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

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

代码

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退回了报告

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

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本教程为授权出品教程1) 优秀的程序应该是这样的:阅读时,感觉很优雅;新增功能时,感觉很轻松;运行时,感觉很快速,这就需要设计模式支撑2) 设计模式包含了大量的编程思想,讲授和真正掌握并不容易,网上的设计模式课程不少,大多讲解的比较晦涩,没有真实的应用场景和框架源码支撑,学习后,只知其形,不知其神。就会造成这样结果: 知道各种设计模式,但是不知道怎么使用到真实项目。本课程针对上述问题,有针对性的进行了升级 (1) 授课方式采用 图解+框架源码分析的方式,让课程生动有趣好理解 (2) 系统全面的讲解了设计模式,包括 设计模式七大原则、UML类图-类的六大关系、23种设计模式及其分类,比如 单例模式的8种实现方式、工厂模式的3种实现方式、适配器模式的3种实现、代理模式的3种方式、深拷贝等3) 如果你想写出规范、漂亮的程序,就花时间来学习下设计模式吧课程内容和目标本课程是使用Java来讲解设计模式,考虑到设计模式比较抽象,授课采用 图解+框架源码分析的方式1) 内容包括:设计模式七大原则(单一职责、接口隔离、依赖倒转、里氏替换、开闭原则、迪米特法则、合成复用)、UML类图(类的依赖、泛化和实现、类的关联、聚合和组合) 23种设计模式包括:创建型模式:单例模式(8种实现)、抽象工厂模式、原型模式、建造者模式、工厂模式。结构型模式:适配器模式(3种实现)、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式(3种实现)。行为型模式:模版方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式(Interpreter模式)、状态模式、策略模式、职责链模式(责任链模式)。2) 学习目标:通过学习,学员能掌握主流设计模式,规范编程风格,提高优化程序结构和效率的能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值