I. Definition and composition of template method patterns
The template method pattern is a very simple pattern that can be implemented simply by using Inheritance.
The template method pattern consists of two parts, the first part is the abstract parent class, The second part is the concrete implementation subclass. The algorithm framework for subclasses is typically encapsulated in an abstract parent class, including implementation of some public methods and the order in which all methods in the wrapper subclass are Executed. Subclasses inherit the entire algorithm structure by inheriting this abstract class, and can choose to override the parent Class's METHODS.
second, The first example Of--coffee or Tea
Let's start with a cup of coffee. the steps are usually as Follows:
- Boil the water
- Brew Coffee in boiling water
- Pour the coffee into the cup
- Add sugar and milk
varCoffee =function(){}; Coffee.prototype.boilWater=function() {console.log (' Boil the water ' ); }; Coffee.prototype.brewCoffeeGriends=function() {console.log (' Brew coffee with boiling water ' ); }; Coffee.prototype.pourInCup=function() {console.log (' Pour the coffee into the cup ' ); }; Coffee.prototype.addSugarAndMilk=function() {console.log (' Add sugar and milk ' ); }; Coffee.prototype.init=function(){ this. Boilwater (); this. Brewcoffeegriends (); this. Pourincup (); this. Addsugarandmilk (); }; varCoffee =NewCoffee (); Coffee.init ();
next, start preparing our tea, the steps of making tea are not as big as the steps of the Coffee:
- Boil the water
- Soak tea with boiling water
- Pour the tea into the cup
- Add lemon
varTea =function(){}; Tea.prototype.boilWater=function() {console.log (' Boil the water ' ); }; Tea.prototype.steepTeaBag=function() {console.log (' Soak tea with 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 (); }; varTea =NewTea (); Tea.init ();
After thinking and comparing, we find that the brewing process of coffee and tea is very similar:
Brew Coffee |
Tea |
Boil the water |
Boil the water |
Brew Coffee in boiling water |
Soak tea with boiling water |
Pour the coffee into the cup |
Pour the tea into the cup |
Add sugar and milk |
Add lemon |
We found coffee and tea mainly in the following different Points.
- Different materials. One is coffee, one is tea, but we can abstract them all as "drinks".
- The way the bubbles are Different. Coffee is brewing, and tea is soaked, and we can abstract them all into "bubbles".
- The added seasoning is Different. One is sugar and milk, one is lemon, but we can abstract them all into "spices".
After the abstraction, whether it is brewed coffee or tea, we can be organized into the following four steps:
- Boil the water
- Brew a drink in boiling water
- Pour the drink into the cup
- Add seasoning
You can now create an abstract parent class to represent the entire process of soaking a drink. Whether it is coffee, or tea, we use beverage to Express:
varBeverage =function(){}; Beverage.prototype.boilWater=function() {console.log (' Boil the water ' ); }; Beverage.prototype.brew=function(){};//null method, which should be overridden by subclassesBeverage.prototype.pourInCup =function(){};//null method, which should be overridden by subclassesBeverage.prototype.addCondiments =function(){};//null method, which should be overridden by subclassesBeverage.prototype.init =function(){ this. Boilwater (); this. Brew (); this. Pourincup (); this. Addcondiments (); };
Next we will create coffee and tea, and let them inherit the beverage class. Only the "boil water" behavior can use the Boilwater method in the parent class beverage directly, and other methods need to be overridden in Subclasses.
varCoffee =function(){}; Coffee.prototype=NewBeverage (); Coffee.prototype.brew=function() {console.log (' Brew coffee with boiling water ' ); }; Coffee.prototype.pourInCup=function() {console.log (' Pour the coffee into the cup ' ); }; Coffee.prototype.addCondiments=function() {console.log (' Add sugar and milk ' ); }; varCoffee =NewCoffee (); Coffee.init (); varTea =function(){}; Tea.prototype=NewBeverage (); Tea.prototype.brew=function() {console.log (' Soak tea with boiling water ' ); }; Tea.prototype.pourInCup=function() {console.log (' Pour the tea into the cup ' ); }; Tea.prototype.addCondiments=function() {console.log (' Add lemon ' ); }; varTea =NewTea (); Tea.init ();
The reason Beverage.prototype.init is called a template method is that it encapsulates the algorithm framework of a subclass, which acts as a template for an algorithm that instructs the subclass in what order to execute which METHODS. In the Beverage.prototype.init method, each step in the algorithm is clearly displayed in front of Us.
third, JavaScript does not have the disadvantages and solutions of abstract class
What if our coffee class or tea class forgets to implement one of these 4 methods? Take the Brew method example, if we forget to write the Coffee.prototype.brew method, then when the request coffee the brew of the object, the request will follow the prototype chain to find beverage "parent class" corresponding Beverage.prototype.brew method, and the Beverage.prototype.brew method so far is an empty method, which is clearly not in line with what we need.
There are no static language checks in JavaScript. We do not get any form of warning when writing code, and it is dangerous to rely entirely on the programmer's memory and self-consciousness, especially when we are using the template method pattern, a design pattern that is completely dependent on inheritance.
There are two alternative solutions available BELOW.
- Use the duck type to simulate the interface check to ensure that the parent Class's methods are indeed overridden in the Subclass. however, the simulation interface checks introduce unnecessary complexity and require the programmer to proactively perform these interface checks, which requires us to add some code that is not business-logic-related to the business Code.
- Let Beverage.prototype.brew and other methods throw an exception directly, if you forget to write the Coffee.prototype.brew method because of carelessness, then at least we will get an error when the program Runs:
function () { thrownew Error (' subclass must override Brew method ' ); };
The advantage of the 2nd solution is that it is easy to implement, with little extra cost, and the disadvantage is that we get the wrong information at the point in Time.
four, Hook method
With the template method pattern, we encapsulate the Subclass's algorithmic framework in the parent class. These algorithm frameworks are suitable for most subclasses in normal state, but what if there are some special "personality" subclasses?
Hook methods can be used to solve this problem, and placing hooks is a common means of isolating Changes. We place hooks in a place where the parent class is prone to change, and the hooks can have a default implementation, and it is up to the subclass to decide whether to "hook up". The return result of the Hook method determines the following steps of the template method, that is, the next direction of the program, so that the program has the possibility of change.
Beverage.prototype.customerWantsCondiments =function(){ return true;//seasoning is required by default }; Beverage.prototype.init=function(){ this. Boilwater (); this. Brew (); this. Pourincup (); if( this. customerwantscondiments ()) {//If the hook returns true, the seasoning is required this. Addcondiments (); } };
Do you really need to "inherit"?
The template method pattern is one of the few inheritance-based design patterns, but the JavaScript language does not actually provide true class inheritance, which is achieved through the delegation between the object and the Object. In other words, although we have borrowed from the formal language to provide Class-style inheritance, the template method model learned in this chapter is not very authentic. And in a language as flexible as javascript, is it really necessary to inherit such a heavy weapon in such an example?
Under the guidance of the Hollywood principles, the following code can achieve the same effect as Inheritance.
<script type= "text/javascript" >varBeverage =function(param) {varBoilwater =function() {console.log (' Boil the water ' ); }; varBrew = Param.brew | |function(){ Throw NewError (' must pass the Brew method ' ); }; varPourincup = Param.pourincup | |function(){ Throw NewError (' must pass Pourincup method ') ); }; varaddcondiments = Param.addcondiments | |function(){ Throw NewError (' must pass Addcondiments method ') ); }; varF =function(){}; F.prototype.init=function() {boilwater (); Brew (); Pourincup (); Addcondiments (); }; returnF; }; varCoffee =Beverage ({brew:function() {console.log (' Brew coffee with boiling water ' ); }, pourincup:function() {console.log (' Pour the coffee into the cup ' ); }, addcondiments:function() {console.log (' Add sugar and milk ' ); } }); varTea =Beverage ({brew:function() {console.log (' Soak tea with boiling water ' ); }, pourincup:function() {console.log (' Pour the tea into the cup ' ); }, addcondiments:function() {console.log (' Add lemon ' ); } }); varCoffee =NewCoffee (); Coffee.init (); varTea =NewTea (); Tea.init ();</script>
Summary
The template method pattern is a typical design pattern that improves system extensibility through package Changes. In the traditional object-oriented language, a program that uses a template method pattern, the class of the subclass and the order of execution are constant, so we abstract this part of the logic into the parent class template Method. The method of how the subclass is implemented is mutable, so we encapsulate the logic of this part of the change into Subclasses. By adding new subclasses, We can add new functionality to the system, without altering the abstract parent class and other subclasses, which is also in line with the Open. closure Principle.
But in javascript, we do not need to follow the sample to implement a template method pattern, High-order function is a better choice.
JavaScript Design patterns and development practices template method patterns