基于常用设计模式的业务框架

简介: 基于常用设计模式的业务框架

前言

做开发也有好几年时间了,最近总结和梳理自己在工作中遇到的一些问题,工作中最容易写出BUG的需求就是改造需求了。一个成熟的业务系统是需要经过无数次迭代而成的,也意味着经过很多开发人员之手,最后到你这里,大部分时间都是在梳理代码,理解别人的用意。有的业务功能里面一堆IF嵌套嵌套,耦合度太过,理解起来比较费劲,也容易改出BUG。


不同人有不同的编码习惯,这没有办法,但如果系统中能用一些常用的设计模式来编码,那多多少少能增加可读性,降低耦合度,所以想做出几种常用的设计模式工具类,开发时可以直接使用,让我们更加专注于业务代码开发。

正文

框架基于常用的设计模式,策略模式、模板方法模式、工厂模式、责任链模式等,结合Spring IOC,Spring AOP,Springboot自动装配;

主要有4个通用的设计模式处理器:

通用策略模式处理器

业务场景

购买保险产品的费用计算方法有多种,按日计算、按月计算、按费率表计算。不同产品可选择的计费选项可能不一样,如下:

日计算(01):支持 A产品、B产品

月计算(02):支持 A产品、C产品

费率表计算(03):支持 A产品、B产品、C产品

代码演示

        //计算类型
        String calculateType="01";
        //产品编号
        String productNo="A";
        if(calculateType.equals("01")){
            if ("A,B".contains(productNo)){
                //按日计算
            }
        }else if(calculateType.equals("02")){
            if ("A,C".contains(productNo)){
                //按月计算
            }
        }else if(calculateType.equals("03")){
            if ("A,B,C".contains(productNo)){
                //按费率表计算
            }
        }

上面这种场景如果使用 if…else…处理的话,随着代码不断迭代,其可读性、调整成本会变得越来越大;

下面使用策略模式来演示:

定义处理器,继承策略处理器接口

import com.zdc.business.business.annotation.BComponent;
import com.zdc.business.business.handle.strategy.AbstractBHandle;
import com.zdc.business.business.wrapper.CommonBName;
import java.util.Arrays;
/**
 * @Author:猪大肠
 * @Package:com.example.btest.strategy
 * @Date:2023/5/11 20:14
 * @Wechat:DDOS12345H
 * 按日计算
 */
@BComponent
public class Calculate01Handle extends AbstractBHandle<RequestDto,ResponseDto> {
    @Override
    public boolean before(RequestDto requestDto) {
        return true;
    }
    @Override
    public boolean after(RequestDto requestDto) {
        return true;
    }
    @Override
    public ResponseDto doExecute(RequestDto requestDto) {
        System.out.println("按日计算");
        return null;
    }
    @Override
    public CommonBName getName() {
        //定义该处理器的类型名称,以及支持的别名集;执行时按这两个维度匹配处理器
        return new CommonBName<String>("01", Arrays.asList("A","B"));
    }
}

import com.zdc.business.business.annotation.BComponent;
import com.zdc.business.business.handle.strategy.AbstractBHandle;
import com.zdc.business.business.wrapper.CommonBName;
import java.util.Arrays;
/**
 * @Author:猪大肠
 * @Package:com.example.btest.strategy
 * @Date:2023/5/11 20:14
 * @Wechat:DDOS12345H
 * 按日计算
 */
@BComponent
public class Calculate02Handle extends AbstractBHandle<RequestDto,ResponseDto> {
    @Override
    public boolean before(RequestDto requestDto) {
        return true;
    }
    @Override
    public boolean after(RequestDto requestDto) {
        return true;
    }
    @Override
    public ResponseDto doExecute(RequestDto requestDto) {
        System.out.println("按月计算");
        return null;
    }
    @Override
    public CommonBName getName() {
        //定义该处理器的类型名称,以及支持的别名集;执行时按这两个维度匹配处理器
        return new CommonBName<String>("02", Arrays.asList("A","C"));
    }
}
import com.zdc.business.business.annotation.BComponent;
import com.zdc.business.business.handle.strategy.AbstractBHandle;
import com.zdc.business.business.wrapper.CommonBName;
import java.util.Arrays;
/**
 * @Author:猪大肠
 * @Package:com.example.btest.strategy
 * @Date:2023/5/11 20:14
 * @Wechat:DDOS12345H
 * 按日计算
 */
@BComponent
public class Calculate03Handle extends AbstractBHandle<RequestDto,ResponseDto> {
    @Override
    public boolean before(RequestDto requestDto) {
        return true;
    }
    @Override
    public boolean after(RequestDto requestDto) {
        return true;
    }
    @Override
    public ResponseDto doExecute(RequestDto requestDto) {
        System.out.println("按费率表计算");
        return null;
    }
    @Override
    public CommonBName getName() {
        //定义该处理器的类型名称,以及支持的别名集;执行时按这两个维度匹配处理器
        return new CommonBName<String>("03", Arrays.asList("A","B","C"));
    }
}

定义入参类、出参类;

/**
 * @Author:猪大肠
 * @Package:com.example.btest.adapter
 * @Date:2023/5/10 20:48
 * @Wechat:DDOS12345H
 */
public class ResponseDto {
    //...
}
/**
 * @Author:猪大肠
 * @Package:com.example.btest.adapter
 * @Date:2023/5/10 20:48
 * @Wechat:DDOS12345H
 */
public class ResponseDto {
    //...
}

运行用例

import com.zdc.business.business.context.StrategyBContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
 * @Author:猪大肠
 * @Package:com.example.btest.strategy
 * @Date:2023/5/10 19:21
 * @Wechat:DDOS12345H
 */
public class StartApp {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext("com.example.btest");
        StrategyBContext strategyBContext = (StrategyBContext) applicationContext.getBean("strategyBContext");
        //计算类型
        String calculateType="01";
        //产品编号
        String productNo="A";
        RequestDto requestDto=new RequestDto();
        ResponseDto execute = strategyBContext.invoke(calculateType,productNo,requestDto,ResponseDto.class);
    }
}

d82e3611b1351c4d3791ab5ade674b4.png

通用适配器模式处理器

业务场景

现有公司A和公司B进行投保出单,出完单后需要通知相关人员。

公司A:需要邮件、短信通知投保人;

公司B:需要邮件、短信通知被保人,企信通知业务员;

代码演示

/**
 * @Author:猪大肠
 * @Package:com.example.btest.adapter
 * @Date:2023/5/10 20:48
 * @Wechat:DDOS12345H
 */
@Data
public class RequestDto {
  //定义请求参数
    String companyType;
}
/**
 * @Author:猪大肠
 * @Package:com.example.btest.adapter
 * @Date:2023/5/10 20:48
 * @Wechat:DDOS12345H
 */
public class ResponseDto {
    //定义相应参数
}

定义A公司适配器

import com.zdc.business.business.annotation.BComponent;
import com.zdc.business.business.factory.IAdapterEnumBFactory;
import com.zdc.business.business.handle.adapter.AbstractHandlesAdapter;
/**
 * @Author:猪大肠
 * @Package:com.example.btest.adapter
 * @Date:2023/5/10 20:46
 * @Wechat:DDOS12345H
 */
@BComponent
public class NotifyCompanyA extends AbstractHandlesAdapter<RequestDto, ResponseDto> {
    @Override
    public boolean isSupport(RequestDto context) {
        //该方法用于编写适配条件
        if (context.getCompanyType().equals("A")){
            return true;
        }
        return false;
    }
    @Override
    public ResponseDto execute(RequestDto context) {
        System.out.println("发邮件通知投保人");
        System.out.println("发短信通知投保人");
        return null;
    }
    @Override
    public IAdapterEnumBFactory getType() {
        //定义该适配器归属类型
        return ChannelIAdapterEnumBFactory.CHANNEL_NOTIFY;
    }
}

定义枚举参数

import com.zdc.business.business.factory.IAdapterEnumBFactory;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * @Author:猪大肠
 * @Package:com.example.btest.adapter
 * @Date:2023/5/10 22:30
 * @Wechat:DDOS12345H
 */
@Getter
@AllArgsConstructor
public enum  ChannelIAdapterEnumBFactory implements IAdapterEnumBFactory {
    CHANNEL_NOTIFY("notify",10,"公司消息通知处理器"),
    ;
    String type;
    int priorityOrder;
    String description;
}

定义B公司通知适配器

import com.zdc.business.business.annotation.BComponent;
import com.zdc.business.business.factory.IAdapterEnumBFactory;
import com.zdc.business.business.handle.adapter.AbstractHandlesAdapter;
/**
 * @Author:猪大肠
 * @Package:com.example.btest.adapter
 * @Date:2023/5/10 20:46
 * @Wechat:DDOS12345H
 */
@BComponent
public class NotifyCompanyB extends AbstractHandlesAdapter<RequestDto, ResponseDto> {
    @Override
    public boolean isSupport(RequestDto context) {
        //该方法用于编写适配条件
        if (context.getCompanyType().equals("B")){
            return true;
        }
        return false;
    }
    @Override
    public ResponseDto execute(RequestDto context) {
        System.out.println("发邮件通知投保人");
        System.out.println("发短信通知投保人");
        System.out.println("企信通知业务员");
        return null;
    }
    @Override
    public IAdapterEnumBFactory getType() {
        //定义该适配器归属类型
        return ChannelIAdapterEnumBFactory.CHANNEL_NOTIFY;
    }
}

入口代码

import com.zdc.business.business.context.AdapterBContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
 * @Author:猪大肠
 * @Package:com.example.btest.adapter
 * @Date:2023/5/10 21:15
 * @Wechat:DDOS12345H
 */
public class StratApp {
    public static void main(String[] args) {
        //SpringBoot环境下可直接使用@Autowire
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext("com.example.btest");
        AdapterBContext adapterBContext = (AdapterBContext) applicationContext.getBean("adapterBContext");
        //假设当前是A公司投保
        RequestDto requestDto=new RequestDto();
        requestDto.setCompanyType("A");
        ResponseDto execute = adapterBContext.execute(ChannelIAdapterEnumBFactory.CHANNEL_NOTIFY.type, requestDto, ResponseDto.class);
    }
}


712ed4fbad763ec6c629e0d06afc70a.png

通用责任链模式处理器

业务场景

在录单系统中,录单员填写完资料,通常下一步需要提交审核,而在正式提交审核之前,系统需要校验数据是否符合要求。某些场景下不想完全卡主流程,通常会以软提示的方式在前端进行提醒;现有以下4种软提示校验(从上到下校验顺序):

aa69d8537972cfd96ac3d5d57b505ab.png

为了提高体验,当系统抛出资料A校验后,录单员点击“是”进行重新提交,此时由于前面已经点击了“是”了,此时后端不应该再对点击”是“的校验器进行校验。通常这种需要给每个校验器都设置一个标识,当为“是”时,后端跳过校验,但如果校验场景较多时,那代码将难以维护。


现使用责任链模式来处理以上场景

代码演示

定义好请求参数类和相应参数类

import lombok.AllArgsConstructor;
import lombok.Data;
/**
 * @Author:猪大肠
 * @Package:com.example.btest.adapter
 * @Date:2023/5/10 20:48
 * @Wechat:DDOS12345H
 */
@Data
@AllArgsConstructor
public class RequestDto {
    String data;
/**
 * @Author:猪大肠
 * @Package:com.example.btest.adapter
 * @Date:2023/5/10 20:48
 * @Wechat:DDOS12345H
 */
public class ResponseDto {
}
import com.zdc.business.business.factory.IChainsEnumBFactory;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * @Author:猪大肠
 * @Package:com.example.btest.chain
 * @Date:2023/5/11 21:04
 * @Wechat:DDOS12345H
 */
@Getter
@AllArgsConstructor
public enum OrderCheckEnumBFactory implements IChainsEnumBFactory {
        ORDER_CHECK_SOFT_A("order","checkA",0,"资料A校验器"),
        ORDER_CHECK_SOFT_B("order","checkB",1,"资料B校验器"),
        ORDER_CHECK_SOFT_C("order","checkC",2,"资料C校验器"),
    ;
  //处理器类型,标记所属链
    String type;
  //处理器名称
    String name;
  //优先级顺序
    int priorityOrder;
  //描述
    String description;
}

自定义异常类

import lombok.AllArgsConstructor;
import lombok.Data;
/**
 * @Author:猪大肠
 * @Package:com.example.btest.chain
 * @Date:2023/5/11 21:12
 * @Wechat:DDOS12345H
 */
@AllArgsConstructor
@Data
public class SoftTipException extends RuntimeException{
    private String code;
    private String desc;
}

定义校验器

import com.zdc.business.business.annotation.BComponent;
import com.zdc.business.business.factory.IChainsEnumBFactory;
import com.zdc.business.business.handle.chains.AbstractChainHandle;
/**
 * @Author:猪大肠
 * @Package:com.example.btest.chain
 * @Date:2023/5/11 21:02
 * @Wechat:DDOS12345H
 */
@BComponent
public class CheckAHandle extends AbstractChainHandle<RequestDto,ResponseDto> {
    @Override
    public ResponseDto execute(RequestDto context) {
        System.out.println("校验器A");
        if (context.equals("A")){
            //抛出异常,返回下个处理器名称;下次携带这个名称来找到当前节点
            throw new SoftTipException(getNextNode()==null?"succeed":getNextNode().getHandleName(),"资料A可能存在风险,是否继续提交?");
        }else{
            //调用下个节点校验器
            executeNextNode(context);
        }
        return null;
    }
    @Override
    public IChainsEnumBFactory getType() {
        return OrderCheckEnumBFactory.ORDER_CHECK_SOFT_A;
    }
}
import com.zdc.business.business.annotation.BComponent;
import com.zdc.business.business.factory.IChainsEnumBFactory;
import com.zdc.business.business.handle.chains.AbstractChainHandle;
/**
 * @Author:猪大肠
 * @Package:com.example.btest.chain
 * @Date:2023/5/11 21:02
 * @Wechat:DDOS12345H
 */
@BComponent
public class CheckBHandle extends AbstractChainHandle<RequestDto,ResponseDto> {
    @Override
    public ResponseDto execute(RequestDto context) {
        System.out.println("校验器B");
        if (context.equals("B")){
            //抛出异常,返回下个处理器名称;下次携带这个名称来找到当前节点
            throw new SoftTipException(getNextNode()==null?"succeed":getNextNode().getHandleName(),"资料B可能存在风险,是否继续提交?");
        }else{
            //调用下个节点校验器
            executeNextNode(context);
        }
        return null;
    }
    @Override
    public IChainsEnumBFactory getType() {
        return OrderCheckEnumBFactory.ORDER_CHECK_SOFT_B;
    }
}
import com.zdc.business.business.annotation.BComponent;
import com.zdc.business.business.factory.IChainsEnumBFactory;
import com.zdc.business.business.handle.chains.AbstractChainHandle;
/**
 * @Author:猪大肠
 * @Package:com.example.btest.chain
 * @Date:2023/5/11 21:02
 * @Wechat:DDOS12345H
 */
@BComponent
public class CheckCHandle extends AbstractChainHandle<RequestDto,ResponseDto> {
    @Override
    public ResponseDto execute(RequestDto context) {
        System.out.println("校验器C");
        if (context.equals("C")){
            //抛出异常,返回下个处理器名称;下次携带这个名称来找到当前节点
            throw new SoftTipException(getNextNode()==null?"succeed":getNextNode().getHandleName(),"资料C可能存在风险,是否继续提交?");
        }else{
            //调用下个节点校验器
            executeNextNode(context);
        }
        return null;
    }
    @Override
    public IChainsEnumBFactory getType() {
        return OrderCheckEnumBFactory.ORDER_CHECK_SOFT_C;
    }
}

运行用例

import com.zdc.business.business.context.ChainsBContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
 * @Author:猪大肠
 * @Package:com.example.btest.strategy
 * @Date:2023/5/10 19:21
 * @Wechat:DDOS12345H
 */
public class StartApp {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext("com.example.btest");
        ChainsBContext chainsBContext = (ChainsBContext) applicationContext.getBean("chainsBContext");
        //校验标识
        String checkFlag="checkB";
        if (!"succeed".equals(checkFlag)){
            if ("start".equals(checkFlag)){
                chainsBContext.start("order",new RequestDto(checkFlag),null);
            }
            chainsBContext.execute("order",checkFlag,new RequestDto(checkFlag),null);
        }
    }
}

b89cb5c3e9ac3e23536acf291f47b3a.png

通用代理模式处理器

业务场景

与其它周边系统进行交互时,需要将请求报文和响应报文记录到ES中,方便后续排查,并对请求报文加密加签名,响应报文解密验签;

考虑到复用性等方面,所以这里使用代理模式来增强方法最合适不过了。

代码演示

定义ES日志记录增强器

import com.zdc.business.business.annotation.BComponent;
import com.zdc.business.business.handle.proxy.AbstractBEnhanceIntecepter;
import com.zdc.business.business.wrapper.IntecepterProceedWrapper;
/**
 * @Author:猪大肠
 * @Package:com.example.btest.aop
 * @Date:2023/5/11 22:58
 * @Wechat:DDOS12345H
 */
@BComponent
public class EnhanceEsHandle extends AbstractBEnhanceIntecepter {
    @Override
    public Object execute(IntecepterProceedWrapper ipw) {
        //方法参数
        Object[] args = ipw.getArgs();
        System.out.println("请求参数:"+args[0].toString());
        //调用真正的执行方法
        Object result = ipw.proceed();
        System.out.println("响应参数:"+args[0].toString());
        return result;
    }
}

加解密增强器

import com.zdc.business.business.annotation.BComponent;
import com.zdc.business.business.handle.proxy.AbstractBEnhanceIntecepter;
import com.zdc.business.business.wrapper.IntecepterProceedWrapper;
/**
 * @Author:猪大肠
 * @Package:com.example.btest.aop
 * @Date:2023/5/11 22:58
 * @Wechat:DDOS12345H
 */
@BComponent
public class EnhanceEncryHandle extends AbstractBEnhanceIntecepter {
    @Override
    public Object execute(IntecepterProceedWrapper ipw) {
        //方法参数
        Object[] args = ipw.getArgs();
        System.out.println("对请求报文加密:");
        System.out.println("对请求报文加签:");
        //调用真正的执行方法
        Object result = ipw.proceed();
        System.out.println("对请求报文解密:");
        System.out.println("对请求报文验签:");
        return result;
    }
}

被增强类

import com.zdc.business.business.annotation.InterceptorEnhance;
import org.springframework.stereotype.Component;
/**
 * @Author:猪大肠
 * @Package:com.example.btest.aop
 * @Date:2023/5/11 23:06
 * @Wechat:DDOS12345H
 */
@Component
public class HttpToCompanyA {
    //按顺利指定增强器
    @InterceptorEnhance(intecepter = {EnhanceEsHandle.class,EnhanceEncryHandle.class})
    public String sendInfo(String request){
        return  "{code:\"0\",text:\"成功\"}";
    }
}

运行用例

依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        //打包到本地仓库后,引入使用
        <dependency>
            <groupId>com.zdc.business</groupId>
            <artifactId>business</artifactId>
            <version>0.0.1</version>
        </dependency>
    </dependencies>

总结

本人3年多开发经验,对于各方面认识有限。欢迎老师们指出改进之处,有好的建议或者有想法大家可以交流探讨,一起完善。


目录
相关文章
|
18天前
|
设计模式 开发框架 API
我们在SqlSugar开发框架中,用到的一些设计模式
我们在SqlSugar开发框架中,用到的一些设计模式
|
1月前
|
设计模式 测试技术 Python
《手把手教你》系列基础篇(九十二)-java+ selenium自动化测试-框架设计基础-POM设计模式简介(详解教程)
【7月更文挑战第10天】Page Object Model (POM)是Selenium自动化测试中的设计模式,用于提高代码的可读性和维护性。POM将每个页面表示为一个类,封装元素定位和交互操作,使得测试脚本与页面元素分离。当页面元素改变时,只需更新对应页面类,减少了脚本的重复工作和维护复杂度,有利于团队协作。POM通过创建页面对象,管理页面元素集合,将业务逻辑与元素定位解耦合,增强了代码的复用性。示例展示了不使用POM时,脚本直接混杂了元素定位和业务逻辑,而POM则能解决这一问题。
43 6
|
1月前
|
设计模式 Java 测试技术
《手把手教你》系列基础篇(九十四)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-下篇(详解教程)
【7月更文挑战第12天】在本文中,作者宏哥介绍了如何在不使用PageFactory的情况下,用Java和Selenium实现Page Object Model (POM)。文章通过一个百度首页登录的实战例子来说明。首先,创建了一个名为`BaiduHomePage1`的页面对象类,其中包含了页面元素的定位和相关操作方法。接着,创建了测试类`TestWithPOM1`,在测试类中初始化WebDriver,设置驱动路径,最大化窗口,并调用页面对象类的方法进行登录操作。这样,测试脚本保持简洁,遵循了POM模式的高可读性和可维护性原则。
27 2
|
29天前
|
存储 算法 调度
iLogtail设计模式问题之如何确定定时任务框架的功能边界和目标
iLogtail设计模式问题之如何确定定时任务框架的功能边界和目标
|
1月前
|
设计模式 Java 测试技术
《手把手教你》系列基础篇(九十三)-java+ selenium自动化测试-框架设计基础-POM设计模式实现-上篇(详解教程)
【7月更文挑战第11天】页面对象模型(POM)通过Page Factory在Java Selenium测试中被应用,简化了代码维护。在POM中,每个网页对应一个Page Class,其中包含页面元素和相关操作。对比之下,非POM实现直接在测试脚本中处理元素定位和交互,代码可读性和可维护性较低。
23 0
|
1月前
|
设计模式 存储 缓存
Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
36 0
|
1月前
|
设计模式 缓存 安全
Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
21 0
|
3月前
|
设计模式 算法 Java
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式
[设计模式Java实现附plantuml源码~行为型]定义算法的框架——模板方法模式
|
10月前
|
设计模式 缓存 Java
Spring 框架中都用到了哪些设计模式
Spring框架采用了多种设计模式来实现自己的功能,这主要是为了解决一些常见的软件开发问题。以下是一些Spring框架中使用设计模式的原因:
50 0
|
设计模式 Java Spring
Spring 框架中都用到了哪些设计模式?
Spring 框架中都用到了哪些设计模式?
71 0