命令模式中的命令指的是一個執行某些特定事情的指令。
命令模式使用的場景有:有時候需要向某些對象發送請求,但是并不知道請求的接收者是誰,也不知道請求的操作是什么,此時希望用一種松耦合的方式來設計程序代碼;使得請求發送者和請求接受者消除彼此代碼中的耦合關系。
我們先來列舉生活中的一個列子來說明下命令模式:比如我們經常會在天貓上購買東西,然后下訂單,下單后我就想收到貨,并且希望貨物是真的,對于用戶來講它并關心下單后賣家怎么發貨,當然賣家發貨也有時間的,比如24小時內發貨等,用戶更不關心快遞是給誰派送,當然有的人會關心是什么快遞送貨的; 對于用戶來說,只要在規定的時間內發貨,且一般能在相當的時間內收到貨就可以,當然命令模式也有撤銷命令和重做命令,比如我們下單后,我突然不想買了,我在發貨之前可以取消訂單,也可以重新下單(也就是重做命令);比如我的衣服尺碼拍錯了,我取消該訂單,重新拍一個大碼的。
1. 命令模式的列子
記得我以前剛做前端的那會兒,也就是剛畢業進的第一家公司,進的是做外包項目的公司,該公司一般外包淘寶活動頁面及騰訊的游戲頁面,我們那會兒應該叫切頁面的前端,負責做一些html和css的工作,所以那會兒做騰訊的游戲頁面,經常會幫他們做靜態頁面,比如在頁面放幾個按鈕,我們只是按照設計稿幫騰訊游戲哪方面的把樣式弄好,比如說頁面上的按鈕等事情,比如說具體說明的按鈕要怎么操作,點擊按鈕后會發生什么事情,我們并不知道,我們不知道他們的業務是什么,當然我們知道的肯定會有點擊事件,具體要處理什么業務我們并不知道,這里我們就可以使用命令模式來處理了:點擊按鈕之后,必須向某些負責具體行為的對象發送請求,這些對象就是請求的接收者。但是目前我們并不知道接收者是什么對象,也不知道接受者究竟會做什么事情,這時候我們可以使用命令模式來消除發送者與接收者的代碼耦合關系。
我們先使用傳統的面向對象模式來設計代碼:
假設html結構如下:
~~~
<button id="button1">刷新菜單目錄</button>
<button id="button2">增加子菜單</button>
<button id="button3">刪除子菜單</button>
~~~
JS代碼如下:
~~~
var b1 = document.getElementById("button1"),
b2 = document.getElementById("button2"),
b3 = document.getElementById("button3");
// 定義setCommand 函數,該函數負責往按鈕上面安裝命令。點擊按鈕后會執行command對象的execute()方法。
var setCommand = function(button,command){
button.onclick = function(){
command.execute();
}
};
// 下面我們自己來定義各個對象來完成自己的業務操作
var MenuBar = {
refersh: function(){
alert("刷新菜單目錄");
}
};
var SubMenu = {
add: function(){
alert("增加子菜單");
},
del: function(){
alert("刪除子菜單");
}
};
// 下面是編寫命令類
var RefreshMenuBarCommand = function(receiver){
this.receiver = receiver;
};
RefreshMenuBarCommand.prototype.execute = function(){
this.receiver.refersh();
}
// 增加命令操作
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();
}
// 最后把命令接收者傳入到command對象中,并且把command對象安裝到button上面
var refershBtn = new RefreshMenuBarCommand(MenuBar);
var addBtn = new AddSubMenuCommand(SubMenu);
var delBtn = new DelSubMenuCommand(SubMenu);
setCommand(b1,refershBtn);
setCommand(b2,addBtn);
setCommand(b3,delBtn);
~~~
從上面的命令類代碼我們可以看到,任何一個操作都有一個execute這個方法來執行操作;上面的代碼是使用傳統的面向對象編程來實現命令模式的,命令模式過程式的請求調用封裝在command對象的execute方法里。我們有沒有發現上面的編寫代碼有點繁瑣呢,我們可以使用javascript中的回調函數來做這些事情的,在面向對象中,命令模式的接收者被當成command對象的屬性保存起來,同時約定執行命令的操作調用command.execute方法,但是如果我們使用回調函數的話,那么接收者被封閉在回調函數產生的環境中,執行操作將會更加簡單,僅僅執行回調函數即可,下面我們來看看代碼如下:
代碼如下:
~~~
var setCommand = function(button,func) {
button.onclick = function(){
func();
}
};
var MenuBar = {
refersh: function(){
alert("刷新菜單界面");
}
};
var SubMenu = {
add: function(){
alert("增加菜單");
}
};
// 刷新菜單
var RefreshMenuBarCommand = function(receiver) {
return function(){
receiver.refersh();
};
};
// 增加菜單
var AddSubMenuCommand = function(receiver) {
return function(){
receiver.add();
};
};
var refershMenuBarCommand = RefreshMenuBarCommand(MenuBar);
// 增加菜單
var addSubMenuCommand = AddSubMenuCommand(SubMenu);
setCommand(b1,refershMenuBarCommand);
setCommand(b2,addSubMenuCommand);
~~~
我們還可以如下使用javascript回調函數如下編碼:
~~~
// 如下代碼上的四個按鈕 點擊事件
var b1 = document.getElementById("button1"),
b2 = document.getElementById("button2"),
b3 = document.getElementById("button3"),
b4 = document.getElementById("button4");
/*
bindEnv函數負責往按鈕上面安裝點擊命令。點擊按鈕后,會調用
函數
*/
var bindEnv = function(button,func) {
button.onclick = function(){
func();
}
};
// 現在我們來編寫具體處理業務邏輯代碼
var Todo1 = {
test1: function(){
alert("我是來做第一個測試的");
}
};
// 實現業務中的增刪改操作
var Menu = {
add: function(){
alert("我是來處理一些增加操作的");
},
del: function(){
alert("我是來處理一些刪除操作的");
},
update: function(){
alert("我是來處理一些更新操作的");
}
};
// 調用函數
bindEnv(b1,Todo1.test1);
// 增加按鈕
bindEnv(b2,Menu.add);
// 刪除按鈕
bindEnv(b3,Menu.del);
// 更改按鈕
bindEnv(b4,Menu.update);
~~~
2. 理解宏命令:
宏命令是一組命令的集合,通過執行宏命令的方式,可以一次執行一批命令。
其實類似把頁面的所有函數方法放在一個數組里面去,然后遍歷這個數組,依次
執行該方法的。
代碼如下:
~~~
var command1 = {
execute: function(){
console.log(1);
}
};
var command2 = {
execute: function(){
console.log(2);
}
};
var command3 = {
execute: function(){
console.log(3);
}
};
// 定義宏命令,command.add方法把子命令添加進宏命令對象,
// 當調用宏命令對象的execute方法時,會迭代這一組命令對象,
// 并且依次執行他們的execute方法。
var command = function(){
return {
commandsList: [],
add: function(command){
this.commandsList.push(command);
},
execute: function(){
for(var i = 0,commands = this.commandsList.length; i < commands; i+=1) {
this.commandsList[i].execute();
}
}
}
};
// 初始化宏命令
var c = command();
c.add(command1);
c.add(command2);
c.add(command3);
c.execute(); // 1,2,3
~~~