[TOC]
# 命令模式
一個執(zhí)行某些特定事情的指令。最常見的場景是:有時候需要向某些對象發(fā)送請求,但是并不知道請求的接收者是誰,也不知道被請求的操作是什么。命令模式在JavaScript語言中是一種隱形的模式。
## 傳統(tǒng)面向?qū)ο缶幊陶Z言的命令模式
```javascript
// 創(chuàng)建行為
var MenuBar = {
refresh: function(){
console.log('刷新');
}
};
var SubMenu = {
add: function(){
console.log('增加');
},
del: function(){
console.log('刪除');
}
};
// 把行為封裝在命令中
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 RefreshMenuCommand(MenuBar);
var addSubMenuCommand = new AddSubMenuCommand(SubMenu);
var delSubMenuCommand = new DelSubMenuCommand(SubMenu);
// 執(zhí)行命令
refreshMenuBarCommand.execute();
addSubMenuCommand.execute();
delSubMenuCommand.execute();
```
## JavaScript 的命令模式
```javascript
// 創(chuàng)建行為
var MenuBar = {
refresh: function(){
console.log('刷新');
}
}
// 創(chuàng)建命令
var RefreshMenuBarCommand = function(receiver){
return {
execute: function(){
receiver.refresh();
}
}
};
// 設置命令
var setCommand = function(button, command){
button.onclick = function(){
command.execute();
}
};
var refreshMenuBarCommand = RefreshMenuBarCommand(MenuBar);
setCommand(button1, refreshMenuBarCommand);
```
## 撤銷和重做
撤銷操作的實現(xiàn)一般是給命令對象增加一個名為`unexecude`或者`undo`的方法,在該方法里執(zhí)行`execute`的反向操作。
撤銷可以保留上一次操作的值,當執(zhí)行撤銷命令時,將值傳入為最后一次的值。
重做則可以用一個隊列來保存,將執(zhí)行過的命令都保存在隊列中,之后通過`shift`依次取出命令,再執(zhí)行。
```javascript
var Ryu = {
attack: function(){...},
defense: function(){...},
...
}
// 創(chuàng)建命令
var makeCommand = function(receiver, state){
return function(){
receiver[state]();
}
};
// 創(chuàng)建行為
var commands = {
'100': 'attack',
'97': 'defense',
...
};
// 保存命令的堆棧
var commandStack = [];
document.onkeypress = function(ev){
var keyCode = ev.keyCode;
var command = makeCommand(Ryu, commands[keyCode]); // 獲取命令
if(command){
command(); // 執(zhí)行命名
commandStack.push(command); // 保存命令
}
};
document.getElementById('replay').onclick = function(){
var command;
while(command = commandStack.shift()){ // 從堆棧中依次去除命令并執(zhí)行
command();
}
};
```
## 命令隊列
一個請求需要等待上一個請求結(jié)束后再進行執(zhí)行,此時就需要把命令放入隊列中。可以嘗試通過`發(fā)布-訂閱`模式來配合實現(xiàn)。
## 宏命令
宏命令是一組命令的集合,通過執(zhí)行宏命令的方式,可以一次執(zhí)行一批命令。
定義宏命令`MacroCommand`,它的結(jié)構也很簡單。`macroCommand.add`方法表示把子命令添加進宏命令對象,當調(diào)用宏命令對象的`execute`方法時,會迭代這一組子命令對象,并且依次執(zhí)行它們的`execute`方法
```javascript
var MacroCommand = function(){
return {
commandList: [], // 命令堆棧
add: function(command){ // 添加命令
this.commandList.push(command);
},
execute: function(){ // 執(zhí)行命令
for(var i=0, command; command = this.commandList(i++)){
command.execute();
}
}
};
};
var macroCommand = MacroCommand();
macroCommand.add(command1);
macroCommand.add(command2);
macroCommand.execute();
```
## 智能命令與傻瓜命令
- 傻瓜命令:命名模式都會在`command`對象中保存一個接收者來負責真正執(zhí)行客戶的請求,這種情況下命令對象是“傻瓜式”的,只負責把客戶的請求轉(zhuǎn)交給接收者來執(zhí)行,這種模式的好處是請求發(fā)起者和請求接收者之間盡可能地得到了解耦。
- 智能命令:可以直接實現(xiàn)請求,不需要接收者的存在。沒有接收者的智能命令退化到和`策略模式`非常相近,從代碼結(jié)構上已經(jīng)無法分辨它們,能分辨的只有它們意圖的不同。
