⚠️ this article for nuggets community first contract article, not authorized to forbid reprint


cause

In the middle of June, the lovely product gave me a temporary demand, requiring me to make a Check for the request from the open platform in the business of product creation/update. If the request meets the requirements, I will do a slightly complicated logic processing.

This Easy level of demand how to stop me, less than half a day I Coding, Push, just as I was ready to order a cup of tea began to touch fish, I received an email.

It was a pile of Chinese characters and English, but a few words stood out:

Your code has been rejected.

When I went through a change of mood of dazeless, shocked, incredulous, and finally resigned, I asked the reviewer why my code was rejected, and he said, “The history code is generally very complete. What if new needs have to rely on it

Have you ever thought that doing is the best way to make the code more extensible?” .

I certainly didn’t, so, with a little bit of guilt, I went to the architect and asked him to help me out.


Find a place that looks good and shove it in

Bright Architecture: Kerwin, is this code lazy?

try {
   // Ignore the historical business code, the following is new content
} catch (Exception) {
   // TODO
} finally {
    SkuMainBean retVal = skuMainBridgeService.updateSkuBridgeMainBean(skuMainBean);
    if(retVal ! =null) {// Item creation/modification asynchronous processing logicSimpleThreadPool.executeRunnable(SimpleThreadPool.ThreadPoolName.openSkuHandle, () -> { skuOperateBusinessService.checkOpenSkuReview(retVal); }); }}Copy the code

You see, this piece of Business code does not affect the original function, but also uses the thread pool asynchronous processing, does not affect the overall interface efficiency, and also encapsulates the complex logic in the Business layer, this is called lazy?

Bright Architecture: Do you think the product creation/modification process is important? Is it our core process? The next time you ask for a new product, continue if and build a pyramid? Did I remember you saying you hated if in your code?

Me (whispering) : I hate to see other people’s if, but mine is ok…

Bright structure (angry smile) : I don’t want to talk to you, let’s think about how to change.

PS: The find a suitable location in look 】 this way is we use most frequently, the relatively small, effective development of the most efficient way, but it is the problem of late is not good maintenance, and as demand becomes more, it will become like pyramid, was a very simple method of function, will become the hundreds of thousands of lines of “shit mountain”, Therefore, it needs to be used as appropriate.


Priority check termination

I (started to think) : If the requirement is to stop execution until a certain situation is met, that situation is too simple to prattle on.

Bright architecture: There is something to be said, for example, if you need to return the result of the identifier and the detailed reason, how do you deal with it?

I: directly define a string and return, and then determine whether the string is NULL.

Bright schema: What if it fails and the reason is NULL or an empty string? There is a more elegant solution using generics, such as defining a tuple like this:

public class ValueMsgReturn<A.B> {
    / * * * * / results
    private final A value;

    / * * * * /
    private final B msg;

    public ValueMsgReturn(A value, B msg) {
        this.value = value;
        this.msg = msg;
    }

    // Omit the Get method
}
Copy the code

The advantage of doing this is that it is generic, simple, and doesn’t require you to define duplicate objects. You can see how tasty it is by trying it out in your own code. The overall code looks like this:

// Omit the interference code
ValueMsgReturn<Boolean, String> check = check();
if (check.getValue()) {
    return check.getValue();
}
Copy the code

PS: This is a simpler case, but there are still tricks to optimize the code. See the history article for more details:

How to write minimal code


Simple Observer mode

I (continue to think) : you just that situation is too simple, back to the point, we can use observer mode decoupling for this requirement!

Bright Architecture (hesitation) : No, but think about what code we need to change.

Me: The core of the observer is the notifier + handler. If we use the built-in observer mode in the JDK, the changes are as follows:

  1. Classes in the history code need to be inheritedObservableclass
  2. The new approach is abstracted into a single class based on a single principleObserverinterface
  3. Build a notification relationship between the two at class initialization

Bright architecture: If a logic at the beginning of the design is using the observer pattern, that is good, but the code is not suitable for history, because it is a class containing a large number of other methods, if the future demand in the second need to notice, code it will be more difficult to maintain, after all the JDK observer pattern is to inherit observables, of course, It’s not impossible to be an alternative.

PS: The JDK Observer pattern described above corresponds to JDK version 1.8. See the history article for more details on the Observer pattern

Observer Mode: I’m not monitoring you


AOP

Me: Bright architecture, do you think it’s appropriate to use AOP?

Bright Architecture: What actions do we use AOP for in general?

Me: In my case, it would be used for permission handling, log printing, cache synchronization, special scenario counting, etc.

Bright Architecture: Yes, can you imagine if we had all this business logic stacked in sections? One cut point is fine. Two? Ten? If you make a bad start, everyone else will follow suit, and soon the code will start to look like a spider’s web, so this is something to be avoided.


MQ decoupling

I (suddenly remembered) : By the way, our products will be created/modified with MQ, I just listen to MQ and do business processing.

Bright Architecture: This is certainly doable, but it’s a bit like killing a chicken and killing a bull. After all, we’re dealing with only a small part of MQ, and what if the history code doesn’t send MQ?


Spring Event

Bright Architecture: Have you read about Spring Events?

I: have studied before, really use still quite appropriate here.

PS: Spring Event is the Event notification mechanism in Spring system, its principle can be understood as the observer pattern of Spring implementation.

Note: The simple Observer pattern mentioned above refers to the observer pattern implemented by the JDK (1.8).

// The following is the Demo code
@RestController
public class EventRequest implements ApplicationContextAware {

    private ApplicationContext appContext;

    @RequestMapping("/testEvent")
    public String testEventSave(String name) {
        appContext.publishEvent(new User(this, name));
        return "ok";
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { appContext = applicationContext; }}Copy the code
/ / listener
@Component
public class WebEventListener {

    /** * Only listen for foo and user.class */
    @EventListener(classes = User.class, condition = "#event.name == 'foo'")
    public void listen(User event){
        // TODO
    }

    /** * Listen for user.class */
    @EventListener(classes = User.class)
    public void listen1(User event){
        // TODO}}Copy the code

Bright architecture: Yes, this Demo is a good example of its strengths

  1. We can Publish multiple events in a single method without interfering with each other
  2. Listeners can do basic filtering based on expressions
  3. An event can be listened for repeatedly


Me: Yes, and it also supports asynchronous event handling!

Bright Architecture (pause) : Do you see async support as a unique advantage? Hahaha, even if the event is being listened to synchronously, you can just use the thread pool to process it asynchronously. Being naturally asynchronous is just the icing on the cake. Don’t get confused. Of course, each technology and feature has its own unique usage scenario, and it needs to be used with special attention, such as:

  1. Whether asynchronous processing is allowed on the business (even with a long delay)
  2. Can you completely trust the parameters in the event notification, whether you need to check back, etc.


Is there another way

I (happy) : If I use Spring Events, I only need to change the code a little bit, and the extensibility and maintainability of the code will be improved immediately, but we just talked about so many methods, how do I feel all observer mode?

Bright Architecture: Yes, whether it’s JDK, Spring, AOP, MQ, these are all observer pattern ideas, after all, the observer pattern is characterized by decoupling.

Me: Can’t we use other design pattern ideas?

Bright architecture: yes, of course, but the change may be a little bigger, after all, this class is nearly thousands of lines, or try to add as few things.

Me: For example, what other ways can be used?

Bright frame: Erm… If you want to hear it, go back to your original code:

finally {
    SkuMainBean retVal = skuMainBridgeService.updateSkuBridgeMainBean(skuMainBean);
    if(retVal ! =null) {// Item creation/modification asynchronous processing logicSimpleThreadPool.executeRunnable(SimpleThreadPool.ThreadPoolName.openSkuHandle, () -> { skuOperateBusinessService.checkOpenSkuReview(retVal); }); }}Copy the code

The skuMainBean object in this business method must be handled by the skuMainBean object, because the entire method is operating on it. We can then abstract the policy classes and use the factory to process them. For example:

// Modify the code
finally {
    skuMainBeanFactory.checkAndHandle(skuMainBean);
}

// Factory method
public void checkAndHandle (SkuMainBean skuMainBean) {
    for(Policy set: Policy) {if (check(skuMainBean)) {
        	// TODO}}}Copy the code

Bright architecture: do you think this is also a good extension?

Me: Yes, I suddenly feel that this way is similar to SpringEvent!

Bright architecture (smiled) : taught also, this strategy + interface programming is based on the factory, through the check method to judge whether to need to deal with, while SpringEvent in plain English is spread through the event, the method of direct call to determine whether to need to deal with, the essence is the same, what do you know the future new demand how do you write?

Me (excited) : I know, to write extensible code, like the code I changed today is not good, too garbage!

Bright Architecture (shakes his head, stands up and leaves) : Kerwin, you’re wrong. The historical code you changed today was best practice at the time, just because you ran into problems that the previous designers didn’t consider. We talk about design patterns, talk about the seven principles, talk about don’t overdesign, just for the situation you have now, we may encounter a variety of strange code in the coding process, we can complain, can joke, but remember, don’t just for some needs to turn beautiful code into shit mountain. So you need to learn the ideas of programming, learn the ideas of design.

Me (loudly) : So, architect! What if there was a piece of code that couldn’t be worse!


“Then refactor it! Then write down the author’s name, mercilessly ridicule him! 🤪”

The last

To summarize, if your requirements allow pre-checkout returns, then don’t hesitate to CheckAndReturn! However, if your needs are similar to mine, the following options are recommended:

  1. Leverage MQ decoupling
  2. Use SpringEvent for decoupling
  3. Consider for yourself whether you need a policy class based on current requirements and possible future requirements
  4. The ultimate solution: truly understand the seven principles of programming and common design patterns, and adapt

Allow me to recommend the previous article: Design Patterns In General: From Why Principles are needed to Practice them

If you find this helpful:

  1. Of course, it’s important to support it
  2. In addition, you can search and follow the public number “is Kerwin ah”, together on the road of technology to go down ~ 😋