Business framework based on common design patterns

foreword

I have been doing development for several years. Recently, I have summarized and sorted out some problems I encountered in my work. The easiest requirement to write BUG in my work is the transformation requirement. A mature business system needs to go through countless iterations, which also means that it has gone through the hands of many developers, and finally when you come to you, you spend most of your time sorting out the code and understanding other people's intentions. In some business functions, there are a bunch of nested IFs, and the degree of coupling is too high. It is difficult to understand, and it is easy to fix bugs.

Different people have different coding habits, there is no way, but if some commonly used design patterns can be used for coding in the system, it can increase readability and reduce coupling, so I want to make several commonly used design patterns Tools can be used directly during development, allowing us to focus more on business code development.

text

The framework is based on commonly used design patterns, such as strategy pattern, template method pattern, factory pattern, responsibility chain pattern, etc., combined with Spring IOC, Spring AOP, Springboot automatic assembly;

Github address: Click to view

There are mainly 4 general-purpose design pattern handlers:

Generic Strategy Pattern Processor

Business scene

There are many ways to calculate the cost of purchasing insurance products, such as daily calculation, monthly calculation, and rate table calculation. The billing options available for different products may be different, as follows:

Daily calculation (01): support product A and product B

Monthly calculation (02): Support product A and product C

Rate table calculation (03): Support product A, product B, product C

code demo

        //计算类型
        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 the above scenario is handled with if...else..., as the code continues to iterate, its readability and adjustment costs will become larger and larger;

The following uses the strategy pattern to demonstrate:

Define the processor, inherit the policy processor interface

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"));
    }
}

Define input and output parameters;

/**
 * @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 {
    
    
    //...
}

Run the use case

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);

    }
}

insert image description here

Generic Adapter Pattern Processor

Business scene

Existing company A and company B are insured and issued orders, and relevant personnel need to be notified after the order is issued.

Company A: needs to notify the policyholder by email or text message;

Company B: It is necessary to notify the insured by email or SMS, and the enterprise letter to notify the salesman;

code demo

/**
 * @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 {
    
    
    //定义相应参数
}

Define Company A Adapter

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;
    }
}

Define enumeration parameters

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;
}

Define B Corp Notification Adapter

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;
    }
}

entry code

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);

    }
}

insert image description here

Generic Chain of Responsibility Pattern Handler

Business scene

In the recorder system, after the recorder fills in the information, usually the next step is to submit it for review, and before formally submitting it for review, the system needs to verify whether the data meets the requirements. In some scenarios, if you don’t want to completely get stuck in the main process, you will usually be reminded on the front end in the form of soft prompts; there are currently 4 types of soft prompt verification (from top to bottom verification order):

insert image description here

In order to improve the experience, when the system throws out the verification of data A, the recorder clicks "Yes" to resubmit. At this time, because "Yes" has been clicked before, the backend should not click "Yes" at this time. Validator for validation. Usually this needs to set a flag for each validator. When it is "yes", the backend skips the validation, but if there are many validation scenarios, the code will be difficult to maintain.

Now use the chain of responsibility model to handle the above scenarios

code demo

Define the request parameter class and the corresponding parameter class

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;
}

custom exception class

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;

}

define validator

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;
    }
}

Run the use case

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);

        }

    }
}

insert image description here

Generic Proxy Mode Processor

Business scene

When interacting with other peripheral systems, it is necessary to record the request message and response message in ES to facilitate subsequent investigation, and encrypt and sign the request message, and decrypt and verify the response message;

Considering the reusability and other aspects, it is most appropriate to use the proxy mode to enhance the method here.

code demo

Define ES logging enhancers

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;
    }
}

Encryption and Decryption Enhancer

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;
    }
}

enhanced class

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:\"成功\"}";
    }

}

Run the use case

insert image description here

rely

    <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>

Summarize

I have more than 3 years of development experience, and I have limited knowledge of all aspects. Teachers are welcome to point out the improvements. If you have good suggestions or ideas, you can exchange and discuss and improve together.

Guess you like

Origin blog.csdn.net/weixin_45031612/article/details/130662041