This is the 8th day of my participation in the August More Text Challenge

The template method pattern is a very simple pattern that can be implemented using inheritance alone. It is a typical design pattern to improve system scalability through encapsulation change.

Definition and composition of a template method pattern

The template mode pattern consists of two parts, the first part is the abstract parent class, the second part is the concrete implementation child class. The algorithm framework of a subclass is usually encapsulated 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.

In the template method pattern, the same parts of the subclass implementation are moved up into the parent class, leaving different parts in the subclass implementation, which is a good example of the idea of generalization.

First example — Coffee or Tea

1 Make a cup of coffee first

  1. The water to a boil
  2. Brew coffee in boiling water
  3. Pour the coffee into the cup
  4. Drink milk with sugar
var Coffee = function () {}; Coffee.prototype.boilWater =function () {
  console.log('Boil the water');
};
Coffee.prototype.brewCoffeeGriends = function () {
  console.log('Brew coffee in boiling water');
};
Coffee.prototype.pourInCup = function () {
  console.log('Pour the coffee into the cup');
};
Coffee.prototype.addSugarAndMilk = function () {
  console.log('With sugar and milk');
};
Coffee.prototype.init = function () {
  this.boilWater();
  this.brewCoffeeGriends();
  this.pourInCup();
  this.addSugarAndMilk();
};
var coffee = new Coffee();
coffee.init();
Copy the code

2 Make a pot of tea

  1. The water to a boil
  2. Soak tea leaves in boiling water
  3. Pour the tea into the cup
  4. With lemon
var Tea = function () {}; Tea.prototype.boilWater =function () {
  console.log('Boil the water');
};
Tea.prototype.steepTeaBag = function () {
  console.log('Soak tea leaves in boiling water');
};
Tea.prototype.pourInCup = function () {
  console.log('Pour the tea into the cup');
};
Tea.prototype.addLemon = function () {
  console.log('Add lemon');
};
Tea.prototype.init = function () {
  this.boilWater(); 
  this.steepTeaBag(); 
  this.pourInCup(); 
  this.addLemon();
};
var tea = new Tea(); 
tea.init();
Copy the code

3. Isolate common ground

Above, we found that the brewing process of coffee and tea is almost the same.

coffee Bubble tea
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

Now let’s find out the differences between making coffee and tea:

  • The ingredients are different. One is tea, the other is coffee, both can be abstracted as “drinks”.
  • The way of soaking is different. Coffee is brewed, tea is soaked, can be abstracted as “brewed”.
  • The spices are different. One is sugar and milk, the other is lemon, both can be abstracted as “spices”.

The steps after abstraction are as follows:

  1. The water to a boil
  2. Soak a drink in boiling water
  3. Pour the drink into the glass
  4. Add seasoning

So, the general code after abstraction is:

var Beverage = function () {}; Beverage.prototype.boilWater =function () {
  console.log('Boil the water');
};
// Empty method, should be overridden by subclasses
Beverage.prototype.brew = function () {};// Empty method, should be overridden by subclasses
Beverage.prototype.pourInCup = function () {};// Empty method, should be overridden by subclasses
Beverage.prototype.addCondiments = function () {}; Beverage.prototype.init =function () {
  this.boilWater();
  this.brew();
  this.pourInCup();
  this.addCondiments();
};
Copy the code

4 Create Coffee subclasses and Tea subclasses

Create the coffee and tea classes and have them inherit from the drinks class:

var Coffee = function () {}; Coffee.prototype =new Beverage();
Copy the code

The boilWater method can be used directly in the parent class Beverage. The other methods need to be overwritten in the Coffee subclass:

Coffee.prototype.brew = function () {
  console.log('Brew coffee in boiling water');
};
Coffee.prototype.pourInCup = function () {
  console.log('Pour the coffee into the cup');
};
Coffee.prototype.addCondiments = function () {
  console.log('With sugar and milk');
};
var Coffee = new Coffee(); 
Coffee.init();
Copy the code

At this point, the Coffee class is complete. When the init method of the Coffee object is called, the request will follow the prototype chain because there is no corresponding init method on the Coffee object and the Coffee constructor prototype. Delegated to the init method of Coffee’s parent Beverage prototype. In the above method of Beverage. Prototype. init, the order of making the drink is set, so a cup of coffee can be made successfully.

According to the above steps, according to gourd gourd gourd gourd gourd, can complete the Tea brewing class.

So in the example above, who is the so-called template method? The answer is beverage.prototype.init.

The reason Beverage. Prototype. init is called a template method is that it encapsulates the algorithm framework of the subclass, which acts as a template for the algorithm, instructing the subclass to execute which methods in which order. In the method Beverage.prototype.init, each step within the algorithm is clearly shown to us.

JavaScript has no solution for abstract classes

The template method pattern is a design pattern that relies heavily on abstract classes. JavaScript doesn’t provide support for abstract classes at the language level, so what are the concessions and workarounds it makes when it doesn’t.

  1. The duck type is used to simulate interface checking to ensure that methods of the parent class are actually overridden in subclasses. However, simulated interface checks introduce unnecessary complexity and require the programmer to perform these interface checks voluntarily, which requires us to add some code to the business code that is irrelevant to the business logic.
  2. letBeverage.prototype.brewSuch methods throw an exception directly if you forget to write it because of carelessnessCoffee.prototype.brewMethod, then at least we get an error when the program runs:
Beverage.prototype.brew = function () {
  throw new Error('Subclasses must override brew methods');
};
Beverage.prototype.pourInCup = function () {
  throw new Error('Subclasses must override the pourInCup method');
};
Beverage.prototype.addCondiments = function () {
  throw new Error('Subclasses must override the addCondiments method');
};
Copy the code

Usage scenarios for the template method pattern

In building a series of UI components, the building process of these components is generally as follows:

  1. Initialize onedivThe container;
  2. throughajaxThe request pulls the data of the response;
  3. Render the data todivInside the container, complete the component construction;
  4. Notifies the user that the component has been rendered.

Thus, all four steps can be abstracted into the template methods of the parent class, which can also provide concrete implementations of step 1 and Step 4. When subclasses inherit from the parent class, they simply override steps 2 and 3 in the template method.

Hook method

We have encapsulated the brewing order of Beverage in the category of Beverage. What if someone drinks coffee without seasoning?

Hooks can be used to solve this problem, and placing hooks is a common means of isolating changes. Place hooks in a variable part of the parent class. Hooks can have a default implementation, and it is up to subclasses to decide whether they want to “hook”. The result of the hook method determines the next part of the template method, which is the next direction of the program, so that the program has the possibility to change.

In the brewing Beverage, define the name of the hook as customerWantsCondiments, and then put the hook into the Beverage class:

var Beverage = function () {}; Beverage.prototype.boilWater =function () {
  console.log('Boil the water');
};
Beverage.prototype.brew = function () {
  throw new Error('Subclasses must override brew methods');
};
Beverage.prototype.pourInCup = function () {
  throw new Error('Subclasses must override the pourInCup method');
};
Beverage.prototype.addCondiments = function () {
  throw new Error('Subclasses must override the addCondiments method');
};
Beverage.prototype.customerWantsCondiments = function () {
  return true; // Seasoning is required by default
};
Beverage.prototype.init = function () {
  this.boilWater();
  this.brew();
  this.pourInCup();
  if (this.customerWantsCondiments()) {
    this.addCondiments(); }};var CoffeeWithHook = function () {}; CoffeeWithHook.prototype =new Beverage();
CoffeeWithHook.prototype.brew = function () {
  console.log('Brew coffee in boiling water');
};
// If the hook returns true, seasoning is required
CoffeeWithHook.prototype.pourInCup = function () {
  console.log('Pour the coffee into the cup');
};
CoffeeWithHook.prototype.addCondiments = function () {
  console.log('With sugar and milk');
};
CoffeeWithHook.prototype.customerWantsCondiments = function () {
  return window.confirm(Would you like any dressing, please? ');
};
var coffeeWithHook = new CoffeeWithHook(); 
coffeeWithHook.init();
Copy the code

Hollywood principle

There is no doubt that Hollywood is a paradise for actors, but it also has many new actors who can’t find work. Many new actors go home and wait for a call after handing over their resumes to the agencies. Sometimes the actor gets tired of waiting and calls the acting company to ask about the situation, and the acting company often replies, “Don’t come to me, I’ll call you.”

In design, such rules are known as the Hollywood principle. Under the guidance of this principle, allows the underlying components to tied himself to the high-level component, and high-level component can decide when and how to use these underlying components, high-level component treatment of underlying components, new actors, with performing arts company is “don’t call us, we will call you”.

The template method pattern is a typical use of the Hollywood principle, but it is often applied to other patterns and scenarios, such as the publish  subscription pattern and the callback function.

Is “inheritance” really necessary

The template method pattern is one of the few design patterns based on inheritance, but the JavaScript language doesn’t really provide true class inheritance, which is implemented through object-to-object delegation.

Guided by the Hollywood principle, the following code works just as well as inheritance.

var Beverage = function (param) {
  var boilWater = function () {
    console.log('Boil the water');
  };
  var brew = param.brew || function () {
    throw new Error('Must pass brew method');
  };
  var pourInCup = param.pourInCup || function () {
    throw new Error('pourInCup method must be passed');
  };
  var addCondiments = param.addCondiments || function () {
    throw new Error('Must pass the addCondiments method');
  };
  var F = function () {}; F.prototype.init =function () {
    boilWater();
    brew();
    pourInCup();
    addCondiments();
  };
  return F;
};

var Coffee = Beverage({
  brew: function () {
    console.log('Brew coffee in boiling water');
  },
  pourInCup: function () {
    console.log('Pour the coffee into the cup');
  },
  addCondiments: function () {
    console.log('With sugar and milk'); }});var Tea = Beverage({
  brew: function () {
    console.log('Soak tea leaves in boiling water');
  },
  pourInCup: function () {
    console.log('Pour the tea into the cup');
  },
  addCondiments: function () {
    console.log('Add lemon'); }});var coffee = new Coffee();
coffee.init();

var tea = new Tea();
tea.init();
Copy the code

One last word

If this article is helpful to you, or inspired by the words, help like attention, your support is the biggest motivation I insist on writing, thank you for your support.

Same series of articles

  1. A singleton of JavaScript design patterns
  2. JavaScript design pattern strategy pattern
  3. JavaScript design pattern proxy pattern
  4. Iterator pattern for JavaScript design pattern
  5. Publish – subscribe JavaScript design pattern
  6. JavaScript design mode command mode
  7. A combination of JavaScript design patterns
  8. JavaScript design pattern template method pattern
  9. Meta-patterns for JavaScript design patterns
  10. The JavaScript design pattern’s chain of responsibility pattern
  11. The JavaScript design pattern mediator pattern
  12. Decorator pattern for JavaScript design pattern
  13. JavaScript design pattern state pattern
  14. Adapter pattern for JavaScript design pattern