JavaScript设计模式 - 命令模式
命令模式是最简单和优雅的模式之一,命令模式中的命令(command)指的是一个执行某些特定事情的指令。@H_404_3@
命令模式最常见的应用场景时:有时候需要向某些对象发送请求,但是并不知道请求的接受者是谁,也不知道被请求的操作时什么。此时希望用一种松耦合的方式来设计程序,使得请求发送者和请求接受者能够消除彼此之间的耦合关系。@H_404_3@
设计模式的永恒的主题就是设法找出变化的部分和不变的部分,然后分别对它们进行封装,从而达到解耦合和复用的功能。@H_404_3@
就像去餐厅订餐一样,顾客需要向厨师发出炒菜但请求,但是顾客并不知道厨师的名字和联系方式,也不知道炒菜的方式和步骤。命令模式就吧顾客的请求封装成command对象,也就是订单对象,这个订单对象可以在程序中被四处传播,从各解开了请求发起者和接受者的耦合。(顾客和厨师事先不需要互相了解对方)@H_404_3@
例子-模拟页面按钮分工
假设现在我们页面的功能和需求十分复杂,一部分程序员负责页面布局和样式的编写,而另外一部分程序员则负责编写页面的交互逻辑。就拿页面上的按钮来说,负责编写按钮样式的人要如何编写按钮点击的事件呢?在这里,我们回过头去看我对作者原话的引用,需要向某些对象发送请求,但是并不知道请求的接受者是谁,也不知道被请求的操作时什么。此时希望用一种松耦合的方式来设计程序,使得请求发送者和请求接受者能够消除彼此之间的耦合关系。对了,在这里的话按钮点击一定会触发事件handler,这个地方就是不变的地方,至于发生的事件handler函数内部具体逻辑则是变化的。@H_404_3@
增加子菜单
var mainMenu = {
refresh: function() {
console.log('刷新菜单');
}
};
var subMenu = {
addSubmenu: function () {
console.log('刷新子菜单');
}
};
// 接下来我们需要将上面的逻辑封装在命令类中
var MainMenuCommand = funcition(receive) {
this.receive = receive;
};
MainMenuCommand.prototype.execute = fucniton() {
this.receive&& this.receive.refresh();
};
var SubMenuCommand = function() {
this.receive = receive;
};
SubMenuCommand = function() {
this.receive && this.receive.addSunmenu();
};
var mainMenuCommand = new MainMenuCommand(mainMenu); // 实例化命令类
var subMenuCommand = new SubMenuCommand(subMenu);
var btnOne = document.getElementById('btn-one');
var btnTwo = document.getElementById('btn-two');
setCommand(btnOne,mainMenuCommand); // 设置命令
setCommand(btnTwo,subMenuCommand);
上面我们先是定义了一个设置命令的函数setCommand
它的作用就是给一个button[DOM]
设置一个点击事件的命令。而后我们给出了具体的按钮点击handler
,但是为了消除按钮点击这个行为和按钮点击的handler函数之间的耦合,我们通过了一个command
命令类的方式,将我们的按钮事件封装成命令类,接着传入正确的hander作为参数实例化command
类对象,将实例好的command
传入我们定义好的setCommand
函数,完成命令的绑定。@H_404_3@
JavaScript中的命令模式
上面我们实现一个命令模式是通过传统的构造类的模式,大家都看出来了太繁杂了,引入了command和receive兑现都使得我们的程序编的更加复杂化了,对于JavaScript
来说也许有更好的实现方法。接下来我们用面对对象的方法来在撸一遍代码prg~@H_404_3@
函数
button.onclick = function () {
fn();
}
};
var mainMenu = {
refresh: function() {
console.log('刷新菜单');
}
};
var subMenu = {
addSubmenu: function () {
console.log('刷新子菜单');
}
};
var btnOne = document.getElementById('btn-one');
var btnTwo = document.getElementById('btn-two');
bindClick(btnOne,mainMenu.refresh); // 绑定事件
bindClick(btnTwo,subMenu.addSubmenu);
呼~ 我们看到使用面对对象的方式要比使用传统的方式要简单而去直观的多。@H_404_3@
JavaScript
作为将函数作为一等对象的语言,和策略模式一样,命令模式也早一融入到了JavaScript
语言中。运算快不一定非得放在command.execute
中,也可以封装在普通函数中,就像上面的mainMenu.refresh
,subMenu.addSubmenu
一样,如果一定要请求接受者
的话,我们可以通过闭包来实现这一点。@H_404_3@
菜单');
}
};
var RefreshMainMenuCommand = funciton(receive) {
return function() {
receive.refresh();
}
};
var refreshMainMenuCommand = RefreshMainMenuCommand(mainMenu); // 这里返回一个闭包 包含请求的接受者
var btnOne = document.getElementById('btn-one');
setCommond(btnOne,refreshMainMenuCommand); 设置命令
通过闭包轻松的实现了请求的接受者,当然目前还没有完善,比如我们还可以加上撤销命令。@H_404_3@
命令模式 - 撤销命令
命令模式 - 撤销和重做
对于一些特别的场景,也许撤销的命令并不是十分适合,这个时候我们就应该想到通过纪录每一次命令,然后重做的方式来完成'伪撤销'功能,并且这样我们可以很方便的撤销到任意一个命令。@H_404_3@
宏命令
一条宏命令,就可以执行一套指令,这个方法在某些场景十分有用,比如作者在讲到了它制作的一款HTML5
游戏就说到了任务的走和跳等就是一系列封装好的指令的集合,也就是宏指令。@H_404_3@