Template pattern in 23 species of common design patterns belong to three categories of behavior model, also is our common design pattern in the day-to-day development and its main meaning is to define abstract classes and declared abstract some basic methods for subclasses implement different logic, at the same time defined in abstract classes encapsulate specific methods to abstract the basic method, that is the template method pattern.

As shown above: Having two methods in a parent class is equivalent to defining a framework. Subclasses override the methods of the parent class, and the template methods that call the parent class are template execution methods to complete the specific business process.

  • Roles in template patterns
  1. Abstract template definition role: Define a set of base methods for subclasses to implement, and define and implement template methods that combine base methods.
  2. Concrete template subclass roles: A basic way to implement abstract template role definitions.
  • Concrete methods in the template pattern
  1. Abstract method: modified by abstract template role declaration and implemented by concrete template role.
  2. Concrete method: declared and implemented by abstract template roles, and not implemented by subclasses. It is usually final and does not allow overrides by specific subclass template roles.
  3. Hook methods (if needed) : declared and implemented by abstract template roles that can be implemented and extended by concrete template roles.

Code examples

Let’s use a real life example to illustrate how the template pattern is embodied in code. We describe a scene of going to the hospital to see a doctor. Generally, the process of going to the hospital to see a doctor is fixed. We need to register first, then go to see the doctor with the registration slip, then carry out various examinations, then go to see the doctor, and finally carry out corresponding treatment. This is a typical template method flow, and the abstract superclass is the flow, and everyone, the subclass, has to go through this flow. Next we use code to implement:

  1. Let’s start by defining an abstract process and defining concrete method calls
public abstract class AbstractHospital {

    /** * Registered */
    protected abstract void register(a);

    /** * See a doctor */
    protected abstract void seeDoc(a);

    /** * all checks */
    protected abstract void checkBody(a);

    /** * treatment */
    protected abstract void verb(a);

    /** * Go to the hospital */
    public final void work(a) { register(); seeDoc(); checkBody(); verb(); }}Copy the code
  1. We then build two instances that inherit the above abstract process
public class Person1Hospital extends AbstractHospital{
    @Override
    protected void register(a) {
        System.out.println("I was the first person to register.");
    }

    @Override
    protected void seeDoc(a) {
        System.out.println("I'm the first person to see a doctor.");
    }

    @Override
    protected void checkBody(a) {
        System.out.println("I'm the first person to get a physical.");
    }

    @Override
    protected void verb(a) {
        System.out.println("I'm the first person to start therapy."); }}Copy the code
public class Person2Hospital extends AbstractHospital{
    @Override
    protected void register(a) {
        System.out.println("I'm the second person to register.");
    }

    @Override
    protected void seeDoc(a) {
        System.out.println("I'm the second person at the doctor's.");
    }

    @Override
    protected void checkBody(a) {
        System.out.println("I'm the second person taking a physical.");
    }

    @Override
    protected void verb(a) {
        System.out.println("I'm the second person to start therapy."); }}Copy the code
  1. Write a client to make the call
public class Client {
    public static void main(String[] args) {
        Person1Hospital person1Hospital = new Person1Hospital();
        person1Hospital.work();

        System.out.println("-- -- -- -- -- -- -- -- -- -- -");

        Person2Hospital person2Hospital = newPerson2Hospital(); person2Hospital.work(); }}Copy the code

Output result:

I’m the first one to register

I am the first person in the doctor I am the first person in the physical examination I am the first person to start treatment ———– I am the second person to register I am the second person in the doctor I am the second person in the physical examination I am the second person to start treatment

The code above means that two people go to the hospital to see the doctor, no matter what kind of illness you need to follow the above process, so define an algorithm in the parent class to control the process of all subclasses. The implementation is handed over to the subclass, which then calls the parent class’s Work (template method) to work.

Template mode hook method used

In that case, some people came to the hospital after seeing the doctor, the doctor told him to go home and drink more water without taking medicine and treatment, for these patients we do not need to install a unified process to achieve. So how do you control it? We can constrain its behavior through hook functions.

  1. Redefine the abstract template class
public abstract class AbstractHospital2 {

    /** * Registered */
    protected abstract void register(a);

    /** * See a doctor */
    protected abstract void seeDoc(a);

    /** * all checks */
    protected abstract void checkBody(a);

    /** * treatment */
    protected abstract void verb(a);

    // The hook function
    protected boolean checkNeedVerb(a){
        return true;
    }

    /** * Go to the hospital */
    public final void work(a) {
        register();
        seeDoc();
        checkBody();
        if(checkNeedVerb()){ verb(); }}}Copy the code
  1. Define a third person to go to the hospital
public class Person3Hospital extends AbstractHospital2{
    @Override
    protected void register(a) {
        System.out.println("I'm the third person to register.");
    }

    @Override
    protected void seeDoc(a) {
        System.out.println("I'm the third person at the doctor's.");
    }

    @Override
    protected void checkBody(a) {
        System.out.println("I'm the third person to have a physical.");
    }

    @Override
    protected void verb(a) {
        System.out.println("I'm the third person to start therapy.");
    }

    @Override
    protected boolean checkNeedVerb(a) {
        return super.checkNeedVerb();
    }

    protected void setNeedVerb(boolean b){
        super.checkNeedVerb = b; }}Copy the code
  1. Client call
Person3Hospital person3Hospital = new Person3Hospital();
person3Hospital.setNeedVerb(false);
person3Hospital.work();
Copy the code

Output result:

I’m the third person to register

I’m the third person to see the doctor and I’m the third person to have a checkup

The hook function provides flexibility for patients who do not need treatment. The hook function is essentially a subclass method that determines the result of the template method, which makes it much more flexible.

The template method pattern calls the base methods in a certain order within the template method. In the previous example, register () is called, seeDoc () is called, and then checkBody () is called to decide whether to execute the verb(). According to our design conventions, abstract classes are responsible for defining and implementation classes are responsible for implementing concrete logic, while the template method does define some abstract methods, which are implemented by the subclass, and the results of the subclass execution affect the results of the parent class, so its biggest disadvantage is that it increases the difficulty of reading the code. Its biggest advantage is to encapsulate the invariable part, expand the variable part, extract the common code, and facilitate maintenance.

Advantages of the template method pattern

  • To maximize code reuse, the parent class’s template methods and some of the implemented steps are inherited and used directly by subclasses.
  • The template method of the parent class ensures that the structure of the algorithm remains unchanged, while the subclass provides partial implementation of the steps
  • Good scalability: add functions by subclass implementation of basic method extension, in line with the single responsibility principle and open and closed principle.

Practical application of template method pattern

When a process is completed, the process performs a series of steps, the series of steps are essentially the same, but the individual steps may be implemented differently, and the template method pattern is usually considered for handling. When we use the Spring framework, we actually apply the template method pattern. The parent class defines which methods need to be executed at each step of the springIOC container initialization, and it is up to the subclasses to implement them. In addition, the JdbcTemplate we often use also makes extensive use of this design pattern. Let’s take a look at spring first. The core refresh method in SpringIOC is sure to be well known.An abstract method, obtainFreshBeanFactory, and two hook methods, postProcessBeanFactory and onRefresh, are illustrated. Let’s look at the obtainFreshBeanFactory method:

ObtainFreshBeanFactory () is called in the refresh method; Method calls two abstract methods. Concrete abstract method implementations are put into subclasses. The corresponding two subclasses are:

If that doesn’t make sense to you, let’s draw a picture that corresponds to the example that we wrote:Look at this picture. The same color means the same meaning. And that makes a lot of sense.