How to reuse code gracefully with JDK and Spring?

preface

Template, as the name suggests, is a fixed and standardized thing.
Template method pattern is a behavior design pattern. It defines an algorithm framework in superclass, allowing subclasses to rewrite specific steps of the algorithm without modifying the structure.

Scene problem

Suppose we are masters of a beverage shop, we need at least the following two skills

It's so simple. In this way, the steps are similar. My first reaction is to write a business interface. Different drinks can implement the methods, like this

After drawing the class diagram, I suddenly found that there is no difference between the first step and the third step, and making drinks is a process work. When I want to use it, I can directly call a method and execute the corresponding production steps.
It's a quick idea to use an abstract parent class instead of an interface. Put the step method in a large process method makingDrinks(), and the first step is exactly the same as the third step. It's not necessary to implement it in the subclass. The improvements are as follows

Looking at our design, I think it's good. Now we use the same makingDrinks() method to process the production of coffee and tea, and we don't want subclasses to cover this method, so we can declare it as final. We want subclasses to provide different production steps. We must declare it as an abstract method in the parent class. In the first and third steps, we don't want subclasses to rewrite, so we declare it as a non abstract method

public abstract class Drinks {

    void boilWater() {
        System.out.println("Boil the water");
    }

    abstract void brew();

    void pourInCup() {
        System.out.println("Pour into the cup");
    }

    abstract void addCondiments();
    
    public final void makingDrinks() {
        //hot water
        boilWater();
        //Brew
        brew();
        //Pour it into the cup
        pourInCup();
        //Feeding
        addCondiments();
    }
}

Next, we deal with coffee and tea respectively. These two classes only need to inherit the parent class and rewrite the abstract methods (realize their respective brewing and seasoning)

public class Tea extends Drinks {
    @Override
    void brew() {
        System.out.println("Make tea");
    }
    @Override
    void addCondiments() {
        System.out.println("Add lemon slices");
    }
}
public class Coffee extends Drinks {
    @Override
    void brew() {
        System.out.println("Make coffee powder");
    }

    @Override
    void addCondiments() {
        System.out.println("Add milk and sugar");
    }
}

Now you can go to work. Try making coffee and tea

public static void main(String[] args) {
    Drinks coffee = new Coffee();
    coffee.makingDrinks();
    System.out.println();
    Drinks tea = new Tea();
    tea.makingDrinks();
}

OK, learn another design pattern, which is the template method pattern. Our makingDrinks() is the template method. We can see that the same steps boilWater() and pourInCup() are only carried out in the parent class, and different steps are implemented in the child class.

Recognition template method

The beginning of Dr. Yan Hong's Book JAVA and patterns describes the Template Method pattern as follows:

The template method pattern is the behavior pattern of the class. Prepare an abstract class, implement some logic in the form of concrete methods and concrete constructors, and then declare some abstract methods to force subclasses to implement the remaining logic. Different subclasses can implement these abstract methods in different ways, so as to have different implementations of the remaining logic. This is the purpose of the template method pattern.

A very important thinking point in writing code is "change and invariance". Which functions in the program are changeable and which functions are invariable, we can abstract the unchanged parts for public implementation, separate the changed parts, encapsulate and isolate them with interfaces, or restrict the behavior of subclasses with abstract classes. The template method well reflects this.
The template method defines the steps of an algorithm and allows subclasses to provide implementation for one or more steps.
Template method pattern is one of the most common patterns among all patterns. It is the basic technology of code reuse based on inheritance. Let's look at the class diagram again

Template method pattern is used to create a template for an algorithm. This template is a method. This method defines the algorithm as a set of steps, in which any step may be abstract and implemented by subclasses. This can ensure that the structure of the algorithm remains unchanged, and some implementations are provided by subclasses.
Let's review our examples of making coffee and tea. Some customers don't want sugar in coffee or lemon in tea. We need to change the template method and ask customers before adding corresponding spices

public abstract class Drinks {

    void boilWater() {
        System.out.println("Boil the water");
    }

    abstract void brew();

    void pourInCup() {
        System.out.println("Pour into the cup");
    }

    abstract void addCondiments();

    public final void makingDrinks() {
        boilWater();
        brew();
        pourInCup();

        //If the customer needs it, add it
        if (customerLike()) {
            addCondiments();
        }
    }

    //Define an empty default method that returns only true
    boolean customerLike() {
        return true;
    }
}

As mentioned above, we have added a logical judgment. The method of logical judgment is a method that only returns true. This method is called hook method.

Hook: in the parent class of the template method, we can define a method that does nothing by default. The subclass can override it according to the situation. This method is called "hook".

Hook methods are generally empty or have a default implementation. The existence of hooks can enable subclasses to hook different points of the algorithm. Whether to link or not depends on the subclass.
Isn't it very useful? Let's look at the making of coffee

public class Coffee extends Drinks {
    @Override
    void brew() {
        System.out.println("Make coffee powder");
    }

    @Override
    void addCondiments() {
        System.out.println("Add milk and sugar");
    }
  //It covers the hook and provides its own inquiry function, allowing the user to input whether feeding is required
    boolean customerLike() {
        String answer = getUserInput();
        if (answer.toLowerCase().startsWith("y")) {
            return true;
        } else {
            return false;
        }
    }

    //Process user input
    private String getUserInput() {
        String answer = null;
        System.out.println("Would you like milk and sugar? input YES or NO");
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        try {
            answer = reader.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (answer == null) {
            return "no";
        }
        return answer;
    }
}

Then test the code and see the results.

I think you should know the benefits of hook. It can be used as a condition control to affect the algorithm flow in abstract classes. Of course, you can also do nothing.
There are many implementations of template methods. Sometimes it may not look like what we call "regular" design. Next, let's take a look at how the template method is used in JDK and Spring.

JDK method in template

When we write code, we often use the comparable comparator to sort array objects. We all implement its compareTo() method, and then we can use collections Sort() or arrays Sort () method.
Don't write the specific implementation class (you can go to github: starfish learning to see my code) and see how to use it

@Override
public int compareTo(Object o) {
    Coffee coffee = (Coffee) o;
    if(this.price < (coffee.price)){
        return -1;
    }else if(this.price == coffee.price){
        return 0;
    }else{
        return 1;
    }
}
public static void main(String[] args) {
  Coffee[] coffees = {new Coffee("Frappuccino",38),
                      new Coffee("Latte",32),
                      new Coffee("Mocha",35)};
 
  Arrays.sort(coffees);

  for (Coffee coffee1 : coffees) {
    System.out.println(coffee1);
  }

}

You might say that it doesn't look like our regular template method, yes. Let's look at the implementation steps of the comparator

  1. Building an array of objects
  2. Via arrays The sort method sorts the array, and the parameters passed are instances of the Comparable interface
  3. The compareTo() method of our implementation class will be called during comparison
  4. Set the ordered array into the original array and sort it

With an ignorant face, this implementation is also a template method.
The focus of this mode is to provide a fixed algorithm framework and let subclasses implement some steps. Although inheritance is a standard implementation method, it is implemented through callback, which can not be said to be a template method.
In fact, AQS, which is the most common in concurrent programming and must be asked in the interview, is a typical template method.

Template method in Spring

There are too many design patterns in Spring, and most extension functions can see the shadow of template mode.
Let's take a look at the template method in IOC container initialization. Whether it is XML or annotation, it is consistent with the core container startup process.
The refresh method of AbstractApplicationContext implements the main logic of IOC container startup.
A refresh() method contains many other step methods. Like the template method, getBeanFactory() and refreshBeanFactory() are abstract methods that must be implemented by subclasses, and postProcessBeanFactory() is a hook method.

public abstract class AbstractApplicationContext extends DefaultResourceLoader
      implements ConfigurableApplicationContext {
 @Override
 public void refresh() throws BeansException, IllegalStateException {
  synchronized (this.startupShutdownMonitor) {
   prepareRefresh();
   ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
   prepareBeanFactory(beanFactory);
            postProcessBeanFactory(beanFactory);
            invokeBeanFactoryPostProcessors(beanFactory);
            registerBeanPostProcessors(beanFactory);
            initMessageSource();
            initApplicationEventMulticaster();
            onRefresh();
            registerListeners();
            finishBeanFactoryInitialization(beanFactory);
            finishRefresh();
  }
 }
    // Two abstract methods
    @Override
 public abstract ConfigurableListableBeanFactory getBeanFactory() throws   IllegalStateException; 
    
    protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
    
    //Hook Method 
    protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
 }
 }

Open your IDEA and we will find that the commonly used ClassPathXmlApplicationContext and AnnotationConfigApplicationContext startup entries are its implementation classes (subclasses of subclasses...).
A subclass of AbstractApplicationContext AbstractRefreshableWebApplicationContext has the implementation of hook method onRefresh():

public abstract class AbstractRefreshableWebApplicationContext extends ...... {
    /**
  * Initialize the theme capability.
  */
 @Override
 protected void onRefresh() {
  this.themeSource = UiApplicationContextUtils.initThemeSource(this);
 }
}

Take a look at the general class diagram:

Summary

  • Advantages: 1. Encapsulate the unchanged part and expand the variable part. 2. Extract public codes for easy maintenance. 3. The behavior is controlled by the parent class and implemented by the child class.
  • Disadvantages: each different implementation requires a subclass to implement, resulting in an increase in the number of classes and a larger system.
  • Usage scenario: 1. There are methods shared by multiple subclasses with the same logic. 2. Important and complex methods can be considered as template methods.
  • Note: in order to prevent malicious operations, the general template method adds the final keyword.

last

Thank you for seeing here. The article has shortcomings. You are welcome to point out; If you think it's good, give me a compliment.

You are also welcome to pay attention to my official account: programmer Maidong. Maidong shares java related technical articles or industry information every day. You are welcome to pay attention to and forward articles!

Tags: Java Programming Spring Design Pattern Algorithm

Posted by thekidscareya on Wed, 18 May 2022 14:59:35 +0300