Responsibility chain mode common code kill if else

Responsibility chain mode common code kill if... else

Today I want to discuss with you how to kill if else.

People who have worked may have a deep understanding: there is nothing that if else can't handle. If so, nest it one more layer.

Most people are engaged in business development, and if else cannot be avoided, but how to make the logic of if else look more pleasing to the eye, more beautiful and easier to maintain?

If you have read three crooked articles before, you may think of the "responsibility chain model".

Yes, it is Responsibility chain model

When you see a lot of if else logic in a Service, you may fantasize about whether to refactor it, but you can't start.

Therefore, today I want to share a template of "universal" responsibility chain model. if else is inserted, I'm sure I can learn it.

When writing design pattern articles before, some students commented that I made things complicated. There was a simple way to do it. Why should I nest so many layers to do these fancy things.

In my opinion, there is no problem to implement it in the simplest way. However, when a certain amount of code is reached, think more about whether someone else can understand it and whether there is a better way, which often requires the ability of "abstraction".

This is why so many people advocate design patterns.

Not much BB, come on.

General implementation of responsibility chain

Now I default that everyone knows what the responsibility chain model is. If you still don't understand this student, you can look at my previous article first.

First, we will have A business executor interface, which will be implemented by all business implementations, which means that logic A, B and C in the figure above will implement this interface

/**
 * Service executor
 * @author Three crooked
 */
public interface BusinessProcess {
    void process(ProcessContext context);
}

You can see that the interface is abnormally simple. There is only one process processing method, which receives ProcessContext

Why does the process method need to receive ProcessContext? Very simply, when dealing with logic A, B and C, logic B may need to rely on the processing results of logic A. So we need A carrier to record these.

Therefore, we have ProcessContext, which represents the context of the responsibility chain.

/**
 * Responsibility chain context
 * @author 3y
 */
public class ProcessContext {
    // code identifying the chain of responsibility
    private String code;
    // The real carrier of storage context
    private Model model;
    // Identification of interruption of responsibility chain
    private Boolean needBreak = false;
}

Now we have the executor of the responsibility chain and the context involved in the responsibility chain, which means that we have the main abstraction of the responsibility chain.

Next, we need to string the chains, so we need a template. In fact, what we do is to string the subclasses of BusinessProcess with a List.

/**
 * Business execution template (string the logic of the responsibility chain)
 * @author 3y
 */
public class ProcessTemplate {
    private List<BusinessProcess> processList;
    public List<BusinessProcess> getProcessList() {
        return processList;
    }
    public void setProcessList(List<BusinessProcess> processList) {
        this.processList = processList;
    }
}

OK, now that we have abstracted the whole responsibility chain, the next step is to expose the process controller to execute the responsibility chain:

/**
 * Process controller of the responsibility chain (general control of the execution process of the whole responsibility chain)
 * @author 3y 
 */
@Data
public class ProcessController {
    
    // Different code s correspond to different responsibility chains
    private Map<String, ProcessTemplate> templateConfig = null;

    public void process(ProcessContext context) {
        //Different responsibility chains are implemented according to the Code of the context
        String businessCode = context.getCode();
        ProcessTemplate processTemplate = templateConfig.get(businessCode);
        List<BusinessProcess> actionList = processTemplate.getProcessList();
        //Traverse the process nodes of a responsibility chain
        for (BusinessProcess action : actionList) {
            try {
                action.process(context);
                if (context.getNeedBreak()) {
                    break;
                }
            } catch (Exception e2) {
                //...
            }
        }
    }
}

We can see that there will be a Map on the process controller common to the execution chain of ProcessController to store the templates of multiple responsibility chains. The advantage of this is that the process controller of ProcessController can support the execution of multiple responsibility chains according to the code.

Next, we have a specific BusinessProcess to join the ProcessTemplate chain, and then call the method of ProcessController to execute the whole push chain.

Generally, we should inject XML. For example, now we have two implementations of BusinessProcess, which are white list and message sending logic:

/**
 * Whitelist processor
 * @author 3y
 */
@Service
public class WhiteListProcess implements BusinessProcess {
    @Override
    public void process(ProcessContext context) {
        UserModel user = (UserModel) context.getModel();
        if ("3y".equals(user.getName())) {
            context.setNeedBreak(true);
        }
    }
}

/**
 * Message processor
 * @author Three crooked
 */
@Service
public class SendMessageProcess implements BusinessProcess {

    @Override
    public void process(ProcessContext context) {
        UserModel user = (UserModel) context.getModel();
        System.out.println("to"+user.getName()+"Send a message");
    }
}

Then we add the above two processors to the template of ProcessTemplate and add ProcessTemplate to the Map of ProcessController:

<!--Chain of responsibility for sending messages-->
<bean id="sendMessageTemplate" class="com.chainofresponsibility.ProcessTemplate">
  <property name="processList">
    <list>
      <ref bean="whiteListProcess"></ref>
      <ref bean="sendMessageProcess"></ref>
    </list>
  </property>
</bean>

<!--General process processor, maintaining multiple responsibility chains-->
<bean id="processController" class="com.chainofresponsibility.ProcessController">
  <property name="templateConfig">
    <map>
      <entry key="sendMessage" value-ref="sendMessageTemplate" />
    </map>
  </property>
</bean>

Then we implement this responsibility chain in the interface:

@RestController
public class UserController {
    @Autowired
    private ProcessController processController;

    @RequestMapping("/send")
    public void  send(String userName) {
        // Build context
        ProcessContext processContext = new ProcessContext();

        UserModel userModel = new UserModel();
        userModel.setAge("24");
        userModel.setName(userName);
        processContext.setModel(userModel);

        processContext.setCode("sendMessage");

        processController.process(processContext);
    }
}

I have made such a big set of things. What functions have I achieved? In fact, it's just an if logic:

if ("3y".equals(userModel.getName())) {
  return;
}
System.out.println("to" + userModel.getName() + "Send a message");

Now let's take a look at the effect. From the function, we can find that as long as we don't enter "3y", the message will be printed

The above logic is actually a set of general responsibility chain code. The core is actually four roles: "business abstract interface", "context during execution", "string business implementation classes" and "a general controller executes responsibility chain"

If you don't understand, sanwai suggests comparing the code again. The design mode of responsibility chain is very easy to use and is also very common in the project.

Just copy the code of BusinessProcess/ProcessContext/ProcessTemplate/ProcessController to your own project, which can help you kill the original if else logic.

Pipeline

I don't know if you have seen the word Pipeline. You may have seen it when learning Redis. In Redis, we will use Pipeline for batch operation.

Despite Redis's Pipeline, from a macro perspective, Pipeline is actually an architectural idea.

At the same time, I also think it is one of the implementations of the "responsibility chain model".

Let's take a look at the architecture diagram of a Pipeline implementation on my side:

It can be seen that the Pipepline implemented by predecessors is still relatively complex, and it is not easy to understand the general responsibility chain mode above. After analysis, it can be seen that it is a change of soup without a change of dressing.

The next time you see the word Pipeline (because it is still very common), you should be able to think of the responsibility chain model, and then you will find that you understand it.

Code GitHub: https://github.com/ZhongFuCheng3y/Java3yTestReposity

Transferred from: https://mp.weixin.qq.com/s?__biz=MzI4Njg5MDA5NA==&mid=2247490601&idx=1&sn=741664a26e8c8d4d3255d19864249bff&chksm=ebd75d28dca0d43e62be820313971e139c24a7fa92b9beb1e91b79a1e3a73b4274acf8a8eec4&token=1936697047&lang=zh_CN#rd

Tags: Design Pattern

Posted by eshban on Tue, 03 May 2022 16:30:39 +0300