There is no best practice in the history of strategy mode! ! !

​1. Background

This situation is often encountered in software development. There are multiple algorithms or strategies to achieve a certain function. We can choose different algorithms or strategies to complete the function according to different environments or conditions. The Strategy Pattern defines a set of strategies, which are encapsulated in different classes. Each strategy can be replaced with each other according to the current scenario, so that the change of the strategy can be independent of the operator. For example, when we go to a certain place, we will choose different modes of travel (shared bicycle, bus, Didi, high-speed rail, airplane, etc.) according to the distance to the target place or the economic situation at hand. These modes of travel are different strategies.

2. Timing of use

After understanding the background, we have a bottom line about the strategy model. The core is to encapsulate different strategies and extract the strategies in different scenarios. Um, this is the logic of if...else... at first glance. In this case, let's take a look at the scene where we first learned code.

if (conditionA) {
    
    
    logicA
} else if (conditionB) {
    
    
    logicB
} else if (conditionC) {
    
    
    logicC
} else {
    
    
    logicD
}

When you start to learn, the problem is not big, as long as you can run. When you have some experience, this code obviously violates the two basic principles of OOP:

  • Single Responsibility Principle (SPR, Single Responsibility Principle): A class or module is only responsible for completing one responsibility or function.
  • Open Closed Principle (OCP): Software entities (modules, classes, methods, etc.) should be "open for extension and closed for modification."

Because these two principles are violated, when the amount of code in the if-else block is relatively large, the subsequent code will become more and more difficult to maintain, and problems will be corrected accidentally. Of course you will ask, Lao Zhou, how can I avoid writing such code! Um, according to Lao Zhou’s experience, when you have two or so layers, you can write like this without over-designing; if there are more than three layers, but each layer has fewer lines of code, you can use guard statements; when there are more than three layers and each layer of code When the amount is relatively large, you need to use the strategy mode.

Three, best practices

1. Demand

For example, on our model training platform, it was only the submission of model input before, and the business side also hopes to support online preview form submission and bind dubbo service to submit to other platforms.

In this case, our form submission must respond to the business side supporting the following three submission types:

  • Submission of model input
  • Online preview form submission
  • Binding dubbo service submission

Iron sons, now we come to a wave of best practices~

2. Define the strategy interface

  • Get strategy type
  • Processing strategy logic
/**
 * 表单提交处理器
 * @param <R> 业务值
 */
public interface FormSubmitHandler<R extends Serializable> {
    
    
    /**
     * 获得提交类型
     * @return 提交类型
     */
    String getSubmitType();/**
     * 处理表单提交请求
     * @param request 请求
     * @return 响应,left:为返回给前端的提示信息,right:为业务值
     */
    CommonPairResponse<String, R> handleSubmit(FormSubmitRequest request);
}
/**
 * 表单提交的请求
 */
@Getter
@Setter
public class FormSubmitRequest {
    
    
    /**
     * 提交类型
     */
    private String submitType;/**
     * 用户id
     */
    private Long userId;/**
     * 表单提交的数据
     */
    private Map<String, Object> formInput;
}

Among them, the getSubmitType method of FormSubmitHandler is used to obtain the submission type (ie strategy type) of the form, which is used to directly obtain the corresponding strategy implementation according to the parameters passed by the client; the relevant parameters passed by the client are all encapsulated as FormSubmitRequest and passed to handleSubmit To process.

3. Implementation of related strategies

Submission of model input:

@Slf4j
@Component
public class ModelSubmitHandler implements FormSubmitHandler<Serializable> {
    
    
    public String getSubmitType() {
    
    
        return "model";
    }public CommonPairResponse<String, Serializable> handleSubmit(FormSubmitRequest request) {
    
    
        log.info("模型提交:userId={}, formInput={}", request.getUserId(), request.getFormInput());
        // 模型创建成功后获得模型的 id
        Long modelId = createModel(request);
        return CommonPairResponse.success("模型提交成功!", modelId);
    }private Long createModel(FormSubmitRequest request) {
    
     // 创建模型的逻辑
        return 123L;
    }
}

Online preview form submission:

@Slf4j
@Component
public class OnlinePreviewSubmitHandler implements FormSubmitHandler<Serializable> {
    
    
    public String getSubmitType() {
    
    
        return "online preview";
    }public CommonPairResponse<String, Serializable> handleSubmit(FormSubmitRequest request) {
    
    
        log.info("在线预览提交:userId={}, formInput={}", request.getUserId(), request.getFormInput());
        return CommonPairResponse.success("在线预览模式提交数据成功!", null);
    }
}

Binding dubbo service submission:

@Slf4j
@Component
public class DubboSubmitHandler implements FormSubmitHandler<Serializable> {
    
    
    public String getSubmitType() {
    
    
        return "dubbo";
    }public CommonPairResponse<String, Serializable> handleSubmit(FormSubmitRequest request) {
    
    
        log.info("dubbo模式提交:userId={}, formInput={}", request.getUserId(), request.getFormInput());
        // 进行dubbo调用,获得业务方返回的提示信息和业务数据
        CommonPairResponse<String, Serializable> response = dubboSubmitData(request);
        return response;
    }
}

4. Establish a simple factory of strategy

@Component
public class FormSubmitHandlerFactory implements InitializingBean, ApplicationContextAware {
    
    
    private static final Map<String, FormSubmitHandler<Serializable>> FORM_SUBMIT_HANDLER_MAP = new HashMap<>();private ApplicationContext applicationContext;/**
     * 根据提交类型获取对应的处理器
     * @param submitType 提交类型
     * @return 提交类型对应的处理器
     */
    public FormSubmitHandler<Serializable> getHandler(String submitType) {
    
    
        return FORM_SUBMIT_HANDLER_MAP.get(submitType);
    }public void afterPropertiesSet() throws Exception {
    
    
        // 将 Spring 容器中所有的 FormSubmitHandler 注册到 FORM_SUBMIT_HANDLER_MAP
        applicationContext.getBeansOfType(FormSubmitHandler.class).values().forEach(
                handler -> FORM_SUBMIT_HANDLER_MAP.put(handler.getSubmitType(), handler)
        );
    }public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
    
        this.applicationContext = applicationContext;
    }
}

We let FormSubmitHandlerFactory implement the InitializingBean interface. In the afterPropertiesSet method, all FormSubmitHandlers are automatically registered to FORM_SUBMIT_HANDLER_MAP based on the Spring container, so that after the Spring container is started, the getHandler method can directly obtain the corresponding form submission processor through submitType.

Factory is only responsible for obtaining Handler, Handler is only responsible for processing specific submissions, Service is only responsible for logical arrangement, so as to achieve functional "low coupling and high cohesion".

There is a sense of wood, the whole process is as smooth as silk~

5. Assumption expansion

If the business side needs to add a submission strategy, such as adding a hook function to submit. This is quite easy for us, we just need to add a new strategy implementation.

@Slf4j
@Component
public class HookSubmitHandler implements FormSubmitHandler<Serializable> {
    
    
    public String getSubmitType() {
    
    
        return "hook";
    }public CommonPairResponse<String, Serializable> handleSubmit(FormSubmitRequest request) {
    
    
        log.info("hook钩子函数提交:userId={}, formInput={}", request.getUserId(), request.getFormInput());
        // 进行 hook 函数调用,并获得业务方返回的提示信息和业务数据
        CommonPairResponse<String, Serializable> response = hookSubmitData(request);
        return response;
    }
}

There is no need to modify any code at this time, because the Spring container will automatically register the HookSubmitHandler in the FormSubmitHandlerFactory when the Spring container is restarted. If the subsequent business party needs to add other strategies, directly add the strategy to achieve it, and there is no need to change the previous code. "Gathering" is simply not too fragrant.


Welcome everyone to pay attention to my official account [ Lao Zhouchao Architecture ], the principle of Java back-end mainstream technology stack, source code analysis, architecture, and various Internet solutions with high concurrency, high performance, and high availability.

Insert picture description here

Guess you like

Origin blog.csdn.net/riemann_/article/details/114108922