[design pattern series 1] template pattern & Strategy Pattern

Describe the usage posture of template mode and policy mode, as well as the differences between them, based on java.

Brother Lou, it's boring to see you write articles every day. Why don't you tell us a joke first. "Well, what are you talking about? Let me think about it first..."

A reporter went to Antarctica to interview a group of penguins. He asked the first penguin, "what do you do every day?"

The penguin said, "eat, sleep and beat peas!"

Then he asked the second penguin. The penguin still said, "eat, sleep and beat peas!"

The reporter asked other penguins with confusion. The answers were the same. In this way, he asked 99 penguins all the time.

When he came to the 100th little penguin, the reporter went over and asked it: what do you do every day?

The little penguin replied, "eat and sleep."

The reporter was surprised and asked, "why don't you beat peas?"

The little penguin glanced at the reporter and said, "I'm Doudou!"

When I first saw this joke, I felt simple and funny, so I had the inspiration of my later article.

Low est mode

If there are three penguins now, they all like "eating, sleeping and playing peas":

public class littlePenguin {
    public void everyDay() {
        System.out.println("having dinner");
        System.out.println("sleep");
        System.out.println("Beat peas with small wings");
    }
}
public class middlePenguin {
    public void everyDay() {
        System.out.println("having dinner");
        System.out.println("sleep");
        System.out.println("Beat peas with a round stomach");
    }
}
public class bigPenguin {
    public void everyDay() {
        System.out.println("having dinner");
        System.out.println("sleep");
        System.out.println("Beat peas with a feather duster");
    }
}
public class test {
    public static void main(String[] args) {
        System.out.println("littlePenguin:");
        littlePenguin penguin_1 = new littlePenguin();
        penguin_1.everyDay();
        
        System.out.println("middlePenguin:");
        middlePenguin penguin_2 = new middlePenguin();
        penguin_2.everyDay();
        
        System.out.println("bigPenguin:");
        bigPenguin penguin_3 = new bigPenguin();
        penguin_3.everyDay();
    }
}

Take a look at the execution results:

littlePenguin:
having dinner
 sleep
 Beat peas with small wings
middlePenguin:
having dinner
 sleep
 Beat peas with a round stomach
bigPenguin:
having dinner
 sleep
 Beat peas with a feather duster

It's easy for us to find the old code in the first step. It's easy for us to use it in the first step of refactoring.

Conventional mode

"Eat, sleep and beat Doudou" are actually independent behaviors. In order not to affect each other (for example, when you suddenly fall asleep during eating, or when you don't sleep well, you are in a hurry to beat Doudou. Just kidding ~ ~), we can simply encapsulate it through the function:

public class littlePenguin {
    public void eating() {
        System.out.println("having dinner");
    }
    public void sleeping() {
        System.out.println("sleep");
    }
    public void beating() {
        System.out.println("Beat peas with small wings");
    }
}
public class middlePenguin {
    public void eating() {
        System.out.println("having dinner");
    }
    public void sleeping() {
        System.out.println("sleep");
    }
    public void beating() {
        System.out.println("Beat peas with a round stomach");
    }
}
//Same as bigPenguin, omit
public class test {
    public static void main(String[] args) {
        System.out.println("littlePenguin:");
        littlePenguin penguin_1 = new littlePenguin();
        penguin_1.eating();
        penguin_1.sleeping();
        penguin_1.beating();
        //The same below, omitting
    }
}

It seems that students who have worked for some time may adopt this implementation method. Do we have a more elegant implementation method?

Template mode

In Template Pattern, an abstract class exposes a method / template that defines how its methods are executed. Its subclasses can override the method implementation as needed, but the call will be made in the way defined in the abstract class. This type of design pattern belongs to behavioral pattern.

The three penguins eat the same food every day and sleep standing up, but the way to beat Doudou is different, so we can abstract "eat, sleep and beat Doudou". Because "eat and sleep" are the same, we can directly implement them, but their way to "beat Doudou" is different, so they are encapsulated into an abstract method, Each Penguin needs to realize the way of "beating peas" separately. Finally, add another method, everyDay(), to fix the daily execution process:

public abstract class penguin {
    public void eating() {
        System.out.println("having dinner");
    }
    public void sleeping() {
        System.out.println("sleep");
    }
    public abstract void beating();
    public void everyDay() {
        this.eating();
        this.sleeping();
        this.beating();
    }
}

Each Penguin realizes its own way of "beating peas":

public class littlePenguin extends penguin {
    @Override
    public void beating() {
        System.out.println("Beat peas with small wings");
    }
}
public class middlePenguin extends penguin {
    @Override
    public void beating() {
        System.out.println("Beat peas with a round stomach");
    }
}
public class bigPenguin extends penguin {
    @Override
    public void beating() {
        System.out.println("Beat peas with a feather duster");
    }
}

Finally, look at the calling method:

public class test {
    public static void main(String[] args) {
        System.out.println("littlePenguin:");
        littlePenguin penguin1 = new littlePenguin();
        penguin1.everyDay();
        System.out.println("middlePenguin:");
        middlePenguin penguin2 = new middlePenguin();
        penguin2.everyDay();
        System.out.println("bigPenguin:");
        bigPenguin penguin3 = new bigPenguin();
        penguin3.everyDay();
    }
}

"Brother Lou, it's hard for you to read the code. Can you draw a UML diagram for me?" "well, actually, drawing a diagram is very troublesome. Who makes brother Lou a warm man? Then I'll learn to draw one for you."

Strategy mode

In Strategy Pattern, the behavior of a class or its algorithm can be changed at run time. This type of design pattern belongs to behavioral pattern. In the policy pattern, we create objects representing various policies and a context object whose behavior changes with the change of policy objects. The policy object changes the execution algorithm of the context object.

Let's first abstract the behavior of three penguins:

public abstract class penguin {
    public void eating() {
        System.out.println("having dinner");
    }
    public void sleeping() {
        System.out.println("sleep");
    }
    public abstract void beating();
}

Each Penguin realizes its own way of "beating peas":

public class littlePenguin extends penguin {
    @Override
    public void beating() {
        System.out.println("Beat peas with small wings");
    }
}
public class middlePenguin extends penguin {
    @Override
    public void beating() {
        System.out.println("Beat peas with a round stomach");
    }
}
public class bigPenguin extends penguin {
    @Override
    public void beating() {
        System.out.println("Beat peas with a feather duster");
    }
}

Here is the key point of the policy mode. Let's look at the definition of the policy mode: "we create objects representing various policies and a context object whose behavior changes with the change of the policy object". Then the context object is as follows:

public class behaviorContext {
    private penguin _penguin;

    public behaviorContext(penguin newPenguin) {
        _penguin = newPenguin;
    }
    public void setPenguin(penguin newPenguin) {
        _penguin = newPenguin;
    }
    public void everyDay() {
        _penguin.eating();
        _penguin.sleeping();
        _penguin.beating();
    }
}

Finally, look at the calling method:

public class test {
    public static void main(String[] args) {
        behaviorContext behavior = new behaviorContext(new littlePenguin());
        behavior.everyDay();

        behavior.setPenguin(new middlePenguin());
        behavior.everyDay();

        behavior.setPenguin(new bigPenguin());
        behavior.everyDay();
    }
}

We can pass different objects to behaviorContext, and then agree on the calling method of everyDay(). In fact, in my example, the policy mode is a little complicated. Because of the pure policy mode, the three penguins only have different methods of bearing (), so we can understand bearing () as different algorithms. The reason why everyDay() is introduced is that it is often used in the actual project scene, that is, the changed algorithm of bearing (), It is packaged into the specific execution process, so the strategy mode does not look so intuitive, but the core idea is the same.

Again, the following UML diagram uses the policy pattern, because I want to talk about it in combination with specific business scenarios. If you want to see the simplest version of the policy pattern, there is no encapsulation of everyDay, only the policy changes performed on beating. You can see the rookie tutorial.

Template mode vs policy mode

When I chose template mode and strategy mode, I found that both can fully meet my needs. Then I went to the Internet to consult a lot of materials, hoping to find two modes. When I chose technology, I could tell me what situations need to choose which mode. It's a shame that I haven't found it yet, because the Internet only tells me the difference between the two implementation postures, However, I didn't explain how to select the specific type. Next, I'll list the core parts of the data I collected for your reference.

Please explain to me the difference between the template method pattern and the policy pattern?

As far as I can tell, they are 99% the same - the only difference is that the template method pattern has an abstract class as the base class, while the strategy class uses the interface implemented by each concrete strategy class.

However, as far as customers are concerned, their consumption patterns are exactly the same – is this correct?

The main difference between the two is the select of the specific algorithm.

When using the Template method pattern, subclassing a Template occurs at compile time. Each subclass provides a different concrete algorithm by implementing the abstract method of the Template. When the client calls the method of the external interface of the Template, the Template calls its abstract method (its internal interface) to call the algorithm as needed.

In contrast, the policy pattern allows you to select algorithm by containment at run time. The specific algorithm is implemented through separate classes or functions, which are passed to the constructor or constructor as parameter passing. For this parameter, select which algorithm will change according to the state of the program or inputdynamic.

in summary:

  • Template method pattern: compile time algorithmselect by subclassing

  • Policy mode: by suppressing runtime algorithmselect

The above is a complete excerpt from the difference description on the Internet. I only see the difference in the realization posture. However, if this can guide me to select the type, I think it is not enough. The following may be more specific:

be similar:

  • Both strategy and template method patterns can be used to meet the opening and closing principle, which makes the software module easy to expand without changing the code.

  • Both patterns represent the separation of a generic function from the detailed implementation of that function. However, there are some differences in the granularity they provide.

Difference:

  • In the policy, the coupling between the customer and the policy is more loose, while in the template method, the two modules are more closely coupled.

  • In the policy, although abstract classes can also be used according to specific situations, most of them use an interface instead of a concrete class, while most of them use abstract classes or concrete classes instead of interfaces in Template methods.

  • In the Strategy pattern, the overall behavior of a class is generally represented by an interface. On the other hand, the Template Method is used to reduce the repetition of less code. The Template code is defined in the basic framework or abstract class. In the Template Method, you can even have a concrete class with a default implementation.

  • In short, you can change the entire algorithm in policy mode, but in Template mode, only some things change (part of the algorithm), while the rest remain unchanged. In the Template Method, the invariant step is implemented in an abstract base class, while the variant step is either the default implementation or not implemented at all. In the Template Method, the component devise r enforces the sorting of steps and steps required by the algorithm, but allows the component client to extend or replace some steps.

After reading the above summary, I still feel that my question has not been answered. Finally, I quote an online differential interpretation:

Template mode:

  • It is based on inheritance.

  • Defines the skeleton of an algorithm that cannot be changed by subclasses. Only certain operations can be overridden in subclasses.

  • The parent class completely controls the algorithm and only distinguishes specific steps from specific classes.

  • Binding is done at compile time.

Policy mode:

  • It is based on authorization / composition.

  • It changes the content of the object by modifying the behavior of the method.

  • It is used to switch between algorithm m families.

  • It changes the behavior of objects at run time by completely replacing one algorithm with another algorithm at run time.

  • Binding completes at run time.

For me with obsessive-compulsive disorder, I haven't found the root of the problem. I always feel something wrong. Let me talk about my understanding of the difference between the two. To tell you the truth, I can see that there are differences in the implementation posture between the two design modes. As for the policy mode to define a unified interface, the template mode does not do so, I don't agree, because I sometimes define a general interface for the template mode. Then others say that the policy mode needs to define a pile of objects, but the template mode does not. If there are 10 different penguins, does the template mode also need to define 10 different penguins, and then implement them specifically for specific methods?

Therefore, I feel that there is no either or division between these two design modes. I just use them as I like. For example, I don't need a fixed execution process. For example, I only need to make a concrete abstraction of a method. I am willing to choose the strategy mode, because I feel that it will make the objects I need to use clearer. If I have a fixed execution process, such as "eat, sleep and beat peas", I prefer to use the template method. I may have seen too much code and get used to it. I prefer to use the template method to standardize the fixed execution process of code.

Of course, I can also combine the two. For example, we can use the template method to realize the three penguins, but for middlePenguin, it may be divided into penguin boy A, penguin boy B and penguin boy C. they all like the penguin sister next door, but they like different ways, including secret love, direct confession and domineering president, I can use the strategy mode to specify their expression to the penguin sister.

Brother is so capricious. He can use it as he likes~~

Actual scene

Any mode needs to be combined with the actual scene to be clearer. These two modes can be found in the projects you have done before. If you pay a little attention, you should find that they actually exist in a large number. For example, many framework codes have many fixed execution processes. Some logic can be processed by default. Some logic needs to be implemented downstream, and then some logic needs to reserve hooks in advance, For example, when executing the process() process, you may need to perform the operation of preProcess(), so this preProcess() is your reserved hook, which can be implemented or not implemented in the downstream.

So after reading this article, you can calm down and think about which of your previous projects use these two modes, and then summarize them in combination with specific scenes. I think you should have a deeper understanding of these two modes.

Postscript

If has been doing business before writing basic code Else, although the design pattern was known for a long time (I read the design pattern by four people three times during my graduate study), I didn't actually write this code. There was always a feeling of exploring flowers in the fog. Then over time, some patterns forgot. When I really need to reconstruct the code, or look at other people's code, I have to check the data, "Oh, I used this design pattern. It's right.". So this time, I intend to sort out all the commonly used design patterns in combination with specific business scenarios. The main reason is that I don't want to be too ambitious. In the future, when code reconstruction, various design patterns can be handy, then my goal has been achieved.

Welcome to the many more articles, please pay attention to the official account of WeChat public, "the road of Lou Tsai", pay attention to it and not lose your way.

Keywords: Design Pattern

Added by james_creasy on Wed, 05 Jan 2022 15:36:16 +0200