JavaScript Design pattern learning and practice (4)

In this article, we summarize the command patterns, combination patterns, and template methods in JavaScript. These patterns are relatively similar.

Command mode

Suppose there is a fast food restaurant, and I am the order waiter of the restaurant, then my day’s work should be like this. When a certain guest orders, he needs to write down his needs on the list and hand it to the kitchen. Guests don’t need to care about which chefs help them cook. The restaurant can also meet the regular service that guests need. For example, guests may be on their way home and ask for an hour before they start cooking. As long as the order is still there, the chef will not forget. Guests can easily cancel their orders, and when there are too many guests, they can also line up in order of order.

The most common application scenarios of the command pattern are: ** Sometimes you need to send a request to some object, but you don’t know who the recipient of the request is or what the requested operation is. At this time, it is hoped to design the program in a loosely coupled way, so that the sender and receiver of the request can eliminate the coupling relationship between each other **.

In terms of eliminating the coupling relationship, in fact, the command mode and the publish-subscribe mode have similar purposes. The command mode is more concerned with executing different command operations according to different command objects, while the publish-subscribe mode is more concerned with changing the messaging mechanism between programs.

Take food ordering as an example, the guest needs to send a request to the chef, but he doesn’t know the name and contact information of these chefs at all, nor the way and steps of the chef to cook. The command pattern encapsulates the guest’s request to order food into a command object, which is the order object in the order object. This object can be passed around in the program, just like an order can be passed from the waiter to the chef. In this way, the customer does not need to know the chef’s name, thus uncoupling the coupling between the caller and receiver of the request.

Example of Command Mode - Menu Program

The theme of Design patterns is always to separate things that are constant from things that change, and Command Patterns are no exception. Some things that happen after pressing a button are constant, while what happens is variable. With the help of the command object, we can easily change this association, so we can also change the language of the button again in the future.

Let’s draw a few buttons first.

1
2
3
4
5
6
7
8
9
10
<body>
< button id = "button1" > click button 1 </button >
< button id = "button2" > click button 2 </button >
< button id = "button3" > click button 3 </button >
</body>
<script>
var button1 = document.getElementById('button1');
var button2 = document.getElementById('button2');
var button3 = document.getElementById('button3');
</script>

Next, define the setCommand function, which is responsible for installing commands on the button. To be sure, clicking the button will execute a command command, and the action of executing the command will be agreed to call the execute method of command. Although it is not known what operations these commands represent, the programmer responsible for drawing the button does not care about these things. He only needs to reserve the interface for installing the command, and the command object knows how to communicate with the object correctly.

1
2
3
4
5
var setCommand = function(button, command) {
button.onclick = function() {
command.execute();
}
}

Finally, the programmers responsible for writing the specific operation of clicking the button submitted their results.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
var MenuBar = {
refresh: function() {
Console.log ('Refresh menu directory')
}
}

var SubMenu = function() {
add: function() {
Console.log ('Add submenu')
},
del: function() {
Console.log ('Delete submenu')
}
}

var RefreshMenuBarCommand = function(receiver) {
this.receiver = receiver;
}

RefreshMenuBarCommand.prototype.execute = function() {
this.receiver.refresh();
}

var AddSubMenuCommand = function(receiver) {
this.receiver = receiver;
}

AddSubMenuCommand.prototype.execute = function() {
this.receiver.add();
}

var DelSubMenuCommand = function(receiver) {
this.receiver = receiver;
}

DelSubMenuCommand.prototype.execute = function() {
this.receiver.del();
}

var refreshMenuBarCommand = new RefreshMenuBarCommand(MenuBar);
var addSubMenuCommand = new AddSubMenuCommand(SubMenu);
var delSubMenuCommand = new DelSubMenuCommand(SubMenu);

setCommand(button1, refreshMenuBarCommand);
setCommand(button2, addSubMenuCommand);
setCommand(button3, delSubMenuCommand);

Command Patterns in JavaScript

Perhaps we will be confused, the so-called command mode, it seems to be a method of the object to take the name of execute, the introduction of command object and receiver of these two out of nothing role is nothing more than to complicate the simple things, the following code can also achieve the same function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var bindClick = function(button, func) {
button.onclick = func;
}

var MenuBar = {
refresh: function() {
Console.log ('Refresh menu directory')
}
}

var SubMenu = function() {
add: function() {
Console.log ('Add submenu')
},
del: function() {
Console.log ('Delete submenu')
}
}

bindClick(button1, MenuBar.refresh);
bindClick(button2, SubMenu.add);
bindClick(button3, SubMenu.del);

This statement is correct. The above code simulates the implementation of the command mode of the traditional Object Oriented language. The command mode encapsulates the procedural request call in the execute method of the command object. By encapsulating the call of the method, the operation block can be wrapped and the command object can be passed around, so when calling the command, the client does not need to care about how things go.

The origin of the command mode is actually an Object Oriented replacement for the callback function.

JavaScript is a language where function is a first-class object. Like the strategy pattern, the command pattern has long been integrated into the JavaScript language. The operation block does not have to be encapsulated in command.execute, it can be encapsulated in a normal function. As a first-class object, function can already be passed around.

In Object Oriented design, the receiver of the command pattern is saved as a property of the command object, and it is agreed that the operation to execute the command calls the command.execute method. In the command pattern using closure, the receiver is enclosed in the environment generated by the closure, and the operation of executing the command can be simpler, just execute the callback function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
var setCommand = function(button, func) {
button.onclick = function() {
func();
}
}

var MenuBar = {
refresh: function() {
Console.log ('Refresh menu directory')
}
}

var RefreshMenuBarCommand = function(receiver) {
return function() {
reciever.refresh();
}
}

var refreshMenuBarCommand = new RefreshMenuBarCommand(MenuBar);

setCommand(button1, refreshMenuBarCommand);

Of course, if you want to more clearly express that you are currently using the command mode, or in addition to executing the command, you may need to provide an operation to undo the command in the future, it is best to set the execution function to execute the execute method

Revoke the order

The function of the command mode is not only to encapsulate the operation block, but also to easily add undo operations to the command object, just like the customer can cancel the order by phone when ordering food.

The goal of this section is to use the Animate class to write an animation that makes the ball move to a certain horizontal position on the page. Now there is an input text box and a button button in the page. Enter some numbers in the text box to represent the horizontal position of the ball after it moves, and the ball starts to move immediately after the user clicks the button.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<body>
<div id="ball"></div>
Enter the position of the ball after moving: < input id = "pos"/>
< button id = "moveBtn" > start moving </button >
</body>

<script>
var ball = document.getElementById('ball');
var pos = document.getElementById('pos');
var moveBtn = document.getElementById('moveBtn');

moveBtn.onclick = function() {
var animate = new Animate(ball);
aninate.start('left', pos.value, 1000, 'strongEaseOut');
}
</script>

If you enter 200 in the text box and click the moveBtn button, you can see that the ball moves smoothly to the position of 200 in the horizontal direction. Now we need a way to restore the ball to the position before moving. Of course, you can also enter -200 in the text box and click the button.

This is a method, but more clumsy, it is best to have a Undo button on the page, click the Undo button Zhizhou, the ball will be able to return to the last position

Before adding the undo button, let’s change the code to command mode first.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var ball = document.getElementById('ball');
var pos = document.getElementById('pos');
var moveBtn = document.getElementById('moveBtn');

var MoveCommand = function(reveiver, pos) {
this.receiver = receiver;
this.pos = pos;
}

MoveCommand.prototype.execute = function() {
this.receiver.start('left', this.pos, 1000, 'strongEaseOut');
}

var moveCommand;

moveBtn.onclick = function() {
var animate = new Animate(ball);
moveCommand = new MoveCommand(animate, pose.value);
moveCommand.execute();
}

The implementation of the undo operation is generally to add a method called unexexute or undo to the command object, in which the reverse operation of execute is performed. Before the execute method makes the ball really start to move, we need to record the current position of the ball first. When the unexecute or undo operation, let the ball return to the position just recorded.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var ball = document.getElementById('ball');
var pos = document.getElementById('pos');
var moveBtn = document.getElementById('moveBtn');
var cancelBtn = document.getElementById('cancelBtn');

var MoveCommand = function(reveiver, pos) {
this.receiver = receiver;
this.pos = pos;
this.oldPos = null;
}

MoveCommand.prototype.execute = function() {
this.receiver.start('left', this.pos, 1000, 'strongEaseOut');
this.oldPos = this.receiver.dom.getBoundingClientRect()[this.receiver.propertyName];
}

MoveCommand.prototype.undo = function() {
this.receiver.start('left', this.oldPos, 1000, 'strongEaseOut');
}

var moveCommand;

moveBtn.onclick = function() {
var animate = new Animate(ball);
moveCommand = new MoveCommand(animate, pose.value);
moveCommand.execute();
}

cancaleBtn.onclick = function() {
moveCommand.undo();
}

Undo and redo

Just now we discussed how to undo a command. Many times, we need to undo a series of commands. For example, in a Go program, we have already played 10 moves, and we need to repent to the fifth step at once. Before this, we can store all the executed steps in a history list, and then reverse the loop to execute the undo of these commands one by one until the 5th command.

However, in some cases, the undo operation cannot be successfully used to return the object to the state before execution. For example, in the canvas painting program, there are some points on the canvas, and we draw N curves between these points to connect these points to each other. But it is difficult for me to define an undo operation to erase a certain curve for the command mode here, because in canvas, erasing a line is relatively difficult.

** The best way at this time is to clear the canvas first, and then execute all the commands that have just been executed. This can also be executed using a stack of history lists. Keeping a log of commands and then executing them repeatedly is a good way to reverse irreversible commands. **

For example, some of our games have a playback function. If we store videos, it may be relatively large, but we can store all commands and play back the game by playing back commands.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
var Ryu = {
attack: function() {
Console.log ('attack');
},
defense: function() {
Console.log ('defense');
},
jump: function() {
Console.log ('Jump');
},
crouch: function() {
Console.log ('squat');
},
}

var makdCommand = function(receiver, state) {
return function() {
recevier[state]();
}
}

var commands = {
'119': 'jump',
'115': 'crouch',
'97': 'defense',
'100': 'attack',
}

var commandStack = [];

document.onkeypress = function(ev) {
var keyCode = ev.keyCode;
var command = makeCommand(Ryu, commands[keyCode]);

if (command) {
command()l
commandStack.push(command);
}
}

document.getElementById('replay').onclick = function() {
var command;
while(command = commandStack.shift()) {
command();
}
}

Macro command

A macro command is a collection of commands. By executing macro commands, a batch of commands can be executed at once.

Imagine a universal remote control, every day home press a special button, you can help us close the door, open the computer and log in QQ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
var closeDoorCommand = {
execute: function() {
Console.log ('Close the door');
}
}

var openCommand = {
execute: function() {
Console.log ('Turn on the computer')
}
}

var openQQCommand = {
execute: function() {
Console.log ('Login QQ')
}
}

var MarcoCommand = function() {
return {
commandList: [],
add: function(command) {
this.commandList.push(command);
},
execute: function() {
for(const command of this.commandList) {
command.execute();
}
}
}
}

var marcoCommand = MarcoCommand();
marcoCommand.add(closeDoorCommand);
marcoCommand.add(openCommand);
marcoCommand.add(openQQCommand);

marcoCommand.execute();

Macro commands are actually the product of the combination of command mode and combination mode. **

Smart Command and Fool Command

The command we just created looks like this:

1
2
3
4
5
var closeDoorCommand = {
execute: function() {
Console.log ('Close the door');
}
}

There is no recevier information in the closeDoorCommand function here, which itself covers the behavior of executing the request, which contradicts the command object we saw before that contains a receiver.

Generally speaking, the command pattern saves a receiver in the command object to be responsible for actually executing the client’s request. This kind of command is “fool-like”, it is only responsible for forwarding the client’s request to the receiver for execution. The advantage of this pattern is the decoupling of the request originator and receiver.

But we can also define some smarter commands that can directly implement requests. This kind of smart object is called “smart command”.

Commands without receivers degenerate to be very similar to the strategy pattern, and it is no longer possible to distinguish them from the code structure, only their purposes can be distinguished. The problem domain pointed to by the strategy pattern is smaller, and the goal of all policy objects is always the same. They are just different means to achieve this goal, and their internal implementation is for algorithms. The intelligent command pattern points to a wider problem domain, and the goals solved by the command object are more divergent. At the same time, the command pattern can undo, redo, and even execute the command object in turn in combination with queue buffering.

Combination mode

In programming, we encounter this requirement that “things are made of similar child objects.” The composition pattern is to use small child objects to build larger objects, and these small child objects may themselves be made of smaller grandchildren.

For example, in the example of the macro command we just mentioned, marcoCommand is a composite object, and closeDoorCommand, openCommand, and openQQCommand are all leaf objects. In the execute method of marcoCommand, it does not actually perform the operation, but traverses all the leaf objects it contains and delegates the real execute request to these leaf objects.

** marcoCommand behaves like a command, but it is really just a “proxy” for a set of real commands. Not a real proxy, although structurally similar, its purpose is not to control access to leaf objects. **

Uses of the combined model

The composition pattern combines objects into a tree structure to represent a “whole-part” hierarchy. In addition to representing the tree structure, another advantage of the composition pattern is that it can be represented by the polymorphism of objects, making the user’s use of single objects and combined objects consistent.

  • Represents a tree structure. Through the example of macros, we can find an advantage of the combination pattern, providing a solution to traverse the tree structure. By calling the execute method of the combination object, the program will recursion call the execute method of the leaf object below the combination object. So our universal remote control only needs one operation to complete the door, open the computer, and open QQ in turn. The combination mode can very conveniently describe the hierarchy of the object part-whole.
  • Utilize the polymorphism of objects to treat combined objects and single objects uniformly. Using the polymorphic performance of objects, the Client can ignore the difference between combined objects and single objects. In the composition mode, the client will use all objects in the composition structure uniformly, regardless of whether it is a combined object or a single object.

This will bring great convenience to customers in actual development. When we add a command to the universal remote control, we don’t care if the command is a macro command or a normal word command, we just need to know that it needs to have an execute method.

More powerful macros

Now we enhance our universal remote control, the function is as follows:

  • Turn on the air conditioner
  • Turn on the TV and stereo
  • Close the door, turn on the computer, log in to QQ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
var MarcoCommand = function() {
return {
commandList: [],
add: function(command) {
this.commandList.push(command);
},
execute: function() {
for(const command of this.commandList) {
command.execute();
}
}
}
}

var openAcCommand = {
execute: function() {
Console.log ('Turn on the air conditioner')
}
}

var openTvCommand = {
execute: function() {
Console.log ('Turn on TV');
}
}

var openSoundCommand = {
execute: function() {
Console.log ('Turn on sound');
}
}

var marcoCommand1 = MarcoCommand();
marcoCommand1.add(openTvCommand);
marcoCommand1.add(openSoundCommand);

var closeDoorCommand = {
execute: function() {
Console.log ('Close the door');
}
}

var openCommand = {
execute: function() {
Console.log ('Turn on the computer')
}
}

var openQQCommand = {
execute: function() {
Console.log ('Login QQ')
}
}

var marcoCommand2 = MarcoCommand();
marcoCommand2.add(closeDoorCommand);
marcoCommand2.add(openCommand);
marcoCommand2.add(openQQCommand);

var marcoCommand = MarcoCommand();
marcoCommand.add(openAcCommand);
marcoCommand.add(marcoCommand1);
marcoCommand.add(marcoCommand2);

marcoCommand.execute();

As can be seen from this example, basic objects can be combined into more complex composite objects, which can be combined by recursion.

The role of abstract classes in composition patterns

The biggest advantage of the combination mode is that it can treat the combination object and the base object consistently. The client does not need to know whether the macro command or the ordinary command is being processed. As long as it is a command and there is an execute method, the command can be added to the tree.

The convenience brought by this transparency is particularly obvious in statically typed languages. For example, in Java, the key to implementing the composition pattern is that both the Composite class and the Leaf class must inherit from a Component abstract class, which represents both composite objects and leaf objects, and it can also ensure that composite objects and leaf objects have methods with the same name.

However, in JavaScript, a dynamically typed language, the polymorphism of objects is innate, and there is no compiler to check the type of variables, so we usually do not simulate a weird abstract class. The point of implementing the composition pattern in JavaScript is to ensure that the composition object and the leaf object have the same method, which usually requires the idea of duck type for interface checking.

Security Issues with Transparency

The transparency of the composition pattern allows the requesting client to ignore the difference between the composition object and the leaf object in the tree, but they are actually different.

Composite objects can have sub-nodes, but leaf objects have no sub-nodes, so we may have some misoperations to add sub-nodes to the leaf object. The solution is usually to add an add method to the leaf object, but this method directly reports an error.

Some noteworthy places

** 1. Combination mode is not a parent-child relationship **

The tree structure of the combination pattern is easy to make people mistakenly think that the combination object and the leaf object are parent-child relationships, which is incorrect.

Composite pattern is a HAS-A relationship, not IS-A. Composite objects contain a set of leaf objects, but Leaf is not a subclass of Composite. Composite objects delegate requests to all the leaf objects they contain, and the key to their cooperation is having the same interface.

** 2. Operations on leaf objects are consistent **

In addition to requiring composite objects and leaf objects to have the same interface, there is a necessary condition that operations on a set of objects must be consistent.

For example, if a company wants to give all employees 1,000 yuan for the New Year’s Day holiday fee, the combination mode can be used in this scenario, but if the company sends a birthday wish to the employee who has a birthday today, the combination mode is useless, unless the birthday is first picked out. The combination mode can only be used if each leaf object in the list is treated in a consistent manner.

** 3. Bidirectional mapping relationship **

The notification steps for issuing savings fees are from the company to each department, to each group, and then to each employee’s mailbox. This in itself is a good example of a combination model, but some employees may belong to multiple organizational structures, and the objects are not strictly hierarchical, and it is not suitable for a combination model.

In this case, we must establish a two-way mapping relationship between parent and child nodes. A simple way is to add collections to both groups and employees to hold each other’s references. But this way of mutual reference is quite complicated, and there is too much coupling between objects, making it difficult to modify and delete an object. ** At this time, we can introduce the mediator pattern, which is a bit similar to the abstraction of immutable information into knowledge layers in the analysis pattern. **

** 4. Use Chain of Responsibility mode to improve portfolio mode performance **

In combination mode, if the structure of the tree is more complex and the number of nodes is large, the performance may not be ideal in the process of traversing the tree. Sometimes we can indeed use some tricks to avoid traversing the entire tree in practice.

One solution is to use the responsibility chain pattern. The responsibility chain pattern generally requires us to manually set up the chain, but in the composite mode, the relationship between parent and child objects naturally forms a chain. Let the request pass along the chain from the parent object to the child object, or vice versa. Until an object that can handle the request is encountered.

When to use combination mode

  • Represents the part-overall structure of an object. Composition mode can easily construct a tree to represent the part-overall structure of an object. Especially when we are not sure how many layers the tree has during development. After the construction of the tree is finally completed, we only need to request the topmost object of the tree to perform unified operations on the entire tree. Adding and deleting nodes in Composition mode is very convenient, which also complies with the Open Closed Principle.

  • The client wants to treat all objects in the tree uniformly. The composition mode allows the client to ignore the difference between the composition object and the leaf object. When the client faces the tree, he does not need to care whether the object currently being processed is a combination object or a leaf object, nor does he need to write a bunch of if-else to handle them. The combination object and the leaf object will each do the right thing.

Template method pattern

There are not many scenarios where inheritance is used in JavaScript development, and many times we like to use mixins.

JavaScript doesn’t have classes, but it can inherit through prototypes. The template approach is an inheritance-based Design pattern.

Definition and composition of template method patterns

The template method 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, and the second part is the concrete implementation subclass. Usually, the algorithm framework of the subclass is encapsulated in the abstract parent class, including implementing some common methods and encapsulating the execution order of all methods in the subclass. Subclasses inherit the entire algorithm structure by inheriting this abstract parent class.

If we have some parallel subclasses, each subclass has some identical behavior and also some different behavior. If the same different behaviors are mixed in each subclass, it means that these same behaviors will be repeated in each subclass. But in fact, the same behavior can be put into a single place. Template methods solve this problem, the same parts of the subclass are moved to the parent class, and the different parts are left to the subclass to implement.

An example - Coffee

Suppose we now want to make a cup of coffee and a cup of tea, the brewing process of the two is compared as follows:

make coffeemake tea
Boil the waterBoil the water
Brew coffee with boiling waterBrew tea with boiling water
Pour coffee into a cupPour tea into a cup
With sugar and milkWith lemon

Let’s write an abstract parent class first

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var Beverage = function() {};

Beverage.prototype.boilWater = function() {
Console.log ('Boil the water');
}

Beverage.prototype.brew = function() {}
Beverage.prototype.pourInCup = function() {}
Beverage.prototype.addCondiments = function() {}

Beverage.prototype.init = function() {
this.boilWater();
this.brew();
this.pourInCup();
this.addCondiments();
}

Then we make tea and coffee separately

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
var Coffee = function(){};

Coffee.prototype = new Beverage();

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');
}

var coffee = new Coffee();
coffee.init();


var Tea = function(){};

Tea.prototype = new Beverage();

Tea.prototype.brew = function() {
Console.log ('Make tea with boiling water');
}

Tea.prototype.pourInCup = function() {
Console.log ('pour the tea into the cup');
}

Tea.prototype.addCondiments = function() {
Console.log ('add lemon');
}

var tea = new Tea();
tea.init();

Beverage.prototype.init is called a template method because it encapsulates the algorithm framework of the subclass, which serves as a template for an algorithm to guide the subclass to execute which methods in which order.

Disadvantages and solutions of not having abstract classes in JavaScript

JavaScript does not provide syntactic support for abstract classes. The first role of an abstract class is to hide the concrete type of an object. Since JavaScript is a “type obfuscation” language, the type of hidden object is not important in JavaScript Red.

On the other hand, when we use prototype inheritance in JavaScript to simulate traditional class-based inheritance, there is no compiler to help us with any form of checking, and there is no way to guarantee that subclasses will override abstract methods in their parent classes.

We offer two alternative solutions:

  • Simulate interface checking with duck types to ensure that the methods of the parent class are indeed overridden in the subclass, but simulating interface checking will bring unnecessary complexity

  • Let methods such as Beverage.prototype.brew throw an exception directly. If we forget to write the’Coffee .prototype.init 'method due to carelessness, we will at least get an error.

Usage scenarios of template methods

In general, this pattern is often used by architects to build the framework of a project. After the architect sets the skeleton of the framework, the programmer inherits the structure of the framework and is responsible for filling in the blanks.

Hook method

With the template method, we encapsulate the algorithm framework of the subclass in the parent class. This framework works fine under normal circumstances. But what if we meet someone who doesn’t like sugar and milk when drinking coffee.

At this time, we need to use the hook method. Placing a hook to isolate changes is a common method, such as:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var Beverage = function() {};

Beverage.prototype.boilWater = function() {
Console.log ('Boil the water');
}

Beverage.prototype.customerWantsCondiments = function() {
return true;
}
Beverage.prototype.brew = function() {
Throw new Error ('Subclass must override brew method')
}
Beverage.prototype.pourInCup = function() {
Throw new Error ('Subclass must override pourInCup method')
}
Beverage.prototype.addCondiments = function() {
Throw new Error ('Subclass must override addCondiments method')
}

Beverage.prototype.init = function() {
this.boilWater();
this.brew();
this.pourInCup();
if (this.customerWantsCondiments()) {
this.addCondiments();
}
}

Do we really need to inherit?

The template method pattern is one of the few inheritance-based Design patterns, but JavaScript does not actually have a real class inheritance. Inheritance is achieved through the delegation between objects. That is to say, although we learn from the form of inheritance. The language of class inheritance, but such template methods are not authentic, and do we need to use inheritance to implement template methods?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
var Beverage = function(params) {
var boilWater = function(){
Console.log ('Boil the water');
}

var brew = params.brew || function() {
Throw new Error ('Subclass must override brew method')
}

var pourInCup = params.pourInCup || function() {
Throw new Error ('Subclass must override pourInCup method')
}

var addCondiments = params.addCondiments || function() {
Throw new Error ('Subclass must override addCondiments method')
}

var F = function() {};

F.prototype.init = function() {
boilWater();
brew();
pourInCup();
addCondiments();
}
}

var Coffee = Beverage({
brew: function() {
Console.log ('Make coffee with boiling water');
},
pourInCup: function() {
Console.log ('pour the coffee into the cup');
},
addCondiments: function() {
Console.log ('Add sugar and milk');
}
})

In JavaScritp, many times higher-order functions are more efficient than template methods.