“This is the 26th day of my participation in the August More Text Challenge.
preface
At the advanced end of the interview, there are often some questions about design patterns, and the answers are not good. Coincides with the activities of more challenges in August, I plan to spend a month to clarify the knowledge points about design patterns, to increase the confidence of my interview.
define
The template method pattern is a very simple pattern that can be implemented using inheritance alone.
The template method pattern consists of two parts, the first part is the abstract parent class, the second part is the concrete implementation child class.
An algorithm framework that encapsulates a subclass in an abstract superclass, including the implementation of some common methods and the execution order of all methods in the subclass.
By inheriting this abstract class, subclasses also inherit the entire algorithm structure and can choose to override the methods of the parent class.
Usage scenarios
In parallel subclasses, there are many identical behaviors among subclasses, but there are also different behaviors among subclasses, and both identical and different behaviors are implemented in a mix of subclasses. The template method pattern can be optimized to move the same parts of the subclass implementation into the parent class, leaving the different parts to be implemented by the subclass.
Abstract classes for headaches
First of all, the template method pattern is a design pattern that relies heavily on abstract classes. JavaScript does not provide support for abstract classes at the language level, and it is difficult to simulate the implementation of abstract classes. The concessions and flexibilities we have to make in the absence of abstract classes.
The role of abstract classes
Abstract class is a concept in Java. In Java, classes are divided into two kinds, one is concrete class, the other is abstract class. Concrete classes can be instantiated, but abstract classes cannot. Since abstract classes cannot be instantiated, if an abstract class is written, it must be inherited by some concrete class.
Use a life scenario to understand. If we go to a convenience store and want to buy a drink, we can’t just say to the owner, “Have a drink.” If we did, the next question would be, “What kind of drinks?” Beverage is just an abstract category, only when we really know the type of beverage, can we get a bottle of coffee, green tea, black tea, cola, etc.
Extraction method and specific method
Abstract methods are declared in abstract classes. Abstract methods have no concrete implementation and are “dumb” methods. When a subclass inherits the abstract class, it must override the parent class’s abstract methods. In addition to abstract methods, if each subclass has the same concrete implementation methods, those methods can also be placed in an abstract class to save code for reuse. These methods are called concrete methods. When code needs to change, we just need to change the concrete methods in the abstract class.
Resolve JavaScript to extract the image class does not support
JavaScript does not provide syntactic support for abstract classes. The first purpose of abstract classes is to hide the concrete type of an object, which is not important in JavaScript because JavaScript is a “type-fuzzy” language.
So when we use prototype inheritance in JavaScript to emulate Java’s class inheritance, there is no compiler to help us do any kind of checking, and there is no way to guarantee that subclasses will override “abstract methods” in their parent classes.
There is a very useful method. That is, throw an error in the method of the abstraction class.
Class Extraction{constructor(){} a(){throw new Error(' subclass must override a method '); }}Copy the code
While no errors are reported while writing the code, they are reported when running the code, just a little late.
Implement a simple template method pattern
Use a classic example to introduce. Coffee and tea.
First use the program to realize the process of making coffee:
Class Coffee{boilWater(){console.log(' boilWater '); } brewCoffeeGriends(){console.log(' Brew coffee with boiling water '); } pourInCup(){console.log(' Pour coffee into cup '); } addSugarAndMilk(){console.log(' addSugarAndMilk '); } init(){ this.boilWater(); this.brewCoffeeGriends(); this.pourInCup(); this.addSugarAndMilk(); } } const coffee = new Coffee(); coffee.init();Copy the code
Then use the program to realize the process of making tea.
Class Tea{boilWater(){console.log(' boilWater '); } steepTeaBag(){console.log(' steep tea in boiling water '); } pourInCup(){console.log(' Pour the tea into the cup '); } addLemon(){console.log(' addLemon '); } init(){ this.boilWater(); this.steepTeaBag(); this.pourInCup(); this.addLemon(); } } const tea = new Tea(); tea.init();Copy the code
Comparing these two programs, it can be found that the steps of making Tea and Coffee are actually similar. Coffee and Tea have the same behaviors, but there are some differences in details. The template method mode can be used to construct a type of Beverage. The behaviors in Coffee and Tea were separated out and moved to the Coffee class Beverage, which was taken as their parent class.
Make tea | coffee | Smoke as |
---|---|---|
The water to a boil | The water to a boil | The water to a boil |
Brew coffee in boiling water | Soak tea leaves in boiling water | Soak the ingredients in boiling water |
Pour the coffee into the cup | Pour the tea into the cup | Pour the drink into the glass |
Add sugar and milk | With lemon | Add the ingredients |
Let’s start by taking a snapshot and comparing the process of making tea and coffee
Make tea | coffee |
---|---|
The water to a boil | The water to a boil |
Brew coffee in boiling water | Soak tea leaves in boiling water |
Pour the coffee into the cup | Pour the tea into the cup |
Add sugar and milk | With lemon |
You can find
- The ingredients are different. One is coffee and one is tea, but we can abstract them both as “drinks.”
- The way of soaking is different. Coffee is brewed, while tea is soaked. We can abstract them both as “brews”.
- The spices are different. One is sugar and milk and one is lemon, but we can abstract them both as “ingredients.”
After abstraction, whether we make coffee or tea, we can organize it into the following four steps:
- The water to a boil
- Brew drinks with boiling water
- Pour the drink into the glass
- Add the ingredients
Create a Beverage that makes a cup of the Beverage
Class Beverage{constructor(){} boilWater(){console.log(' boil the water '); } brew(){// Brew with boiling water, empty method, should be overridden by subclass throw new Error(' subclass must overwrite brew method '); } pourInCup(){// Pour the drink into the cup, empty method, should be overridden by subclasses throw new Error(' subclasses must overwrite pourInCup method '); } addCondiments(){// addCondiments, empty method, should be overridden by subclasses throw new Error(' subclasses must overwrite addCondiments method '); } init(){ this.boilWater(); this.brew(); this.pourInCup(); this.addCondiments(); }}Copy the code
The process of making coffee and tea can inherit the Beverage.
class Coffee extends Beverage{ constructor(){ super(); } boilWater(){console.log(' boilWater '); } brew(){console.log(' brew coffee with boiling water '); } pourInCup(){console.log(' Pour coffee into cup '); } addCondiments(){console.log(' add sugar and milk '); } } const coffee = new Coffee(); coffee.init();Copy the code
This gives us the simplest template-style pattern, where the key is to extract methods from subclasses.