6 命令模式
6.1 命令模式的用途
命令模式的命令指的是一个执行某些特定事情的指令;
命令模式的应用场景:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么。此时希望用一种松耦合的方式来设计程序,使得请求发图送者和请求接收者能够消除彼此之间的耦合关系;
6.2 命令模式的例子——菜单程序
实现一个点击不同按钮调用不同方法的功能 (模拟传统面向对象语言的命令模式实现):
- 按钮绘制:
- 定义
setCommand
函数,setCommand
函数负责往按钮上面安装命令。约定点击按钮会执行某个command
命令,执行命令的动作被约定为调用command
对象的execute()
方法;
var setCommand = function( button, command ){
button.onclick = function(){ command.execute(); }
};
- 编写点击按钮之后的具体行为:
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(){ console.log( '删除子菜单' ); };
- 把命令接收者传入到
command
对象中,并且把command
对象安装到button
上面:
var refreshMenuBarCommand = new RefreshMenuBarCommand( MenuBar );
var addSubMenuCommand = new AddSubMenuCommand( SubMenu );
var delSubMenuCommand = new DelSubMenuCommand( SubMenu );
setCommand( button1, refreshMenuBarCommand );
setCommand( button2, addSubMenuCommand );
setCommand( button3, delSubMenuCommand );
6.3 JavaScript 中的命令模式
var bindClick = function( button, func ){ button.onclick = func; };
var MenuBar = {
refresh: function(){ console.log( '刷新菜单界面' ); }
};
var SubMenu = {
add: function(){ console.log( '增加子菜单' ); },
del: function(){ console.log( '删除子菜单' ); }
};
bindClick( button1, MenuBar.refresh );
bindClick( button2, SubMenu.add );
bindClick( button3, SubMenu.del );
JavaScript 作为将函数作为一等对象的语言,跟策略模式一样,命令模式也早已融入到了 JavaScript 语言之中;运算块不一定要封装在 command.execute
方法中,也可以封装在普通函数中。函数作为一等对象,本身就可以被四处传递。即使我们依然需要请求“接收者”,那也未必使用面向对象的方式,闭包可以完成同样的功能。
6.4 撤消命令
撤销操作的实现一般是给命令对象增加一个名为 unexecude 或者 undo 的方法,在该方法里执行 execute 的反向操作。在 command.execute 方法让小球开始真正运动之前,我们需要先记录小球的当前位置,在 unexecude 或者 undo 操作中,再让小球回到刚刚记录下的位置:
var ball = document.getElementById( 'ball' );
var pos = document.getElementById( 'pos' );
var moveBtn = document.getElementById( 'moveBtn' );
var cancelBtn = document.getElementById( 'cancelBtn' );
var MoveCommand = function( receiver, 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, pos.value );
moveCommand.execute();
};
cancelBtn.onclick = function(){
moveCommand.undo(); // 撤销命令
};
5 宏命令
宏命令是一组命令的集合,通过执行宏命令的方式,可以一次执行一批命令。宏命令对象包含了一组具体的子命令对象,不管是宏命令对象,还是子命令对象,都有一个 execute 方法负责执行命令:
var closeDoorCommand = {
execute: function(){ console.log( '关门' ); }
};
var openPcCommand = {
execute: function(){ console.log( '开电脑' ); }
};
var openQQCommand = {
execute: function(){ console.log( '登录 QQ' ); }
};
var MacroCommand = function(){
return {
commandsList: [],
add: function( command ){ this.commandsList.push( command ); },
execute: function(){
for ( var i = 0, command; command = this.commandsList[ i++ ]; ){
command.execute();
}
}
}
};
var macroCommand = MacroCommand();
macroCommand.add( closeDoorCommand );
macroCommand.add( openPcCommand );
macroCommand.add( openQQCommand );
macroCommand.execute();
6 命令模式小结
JavaScript 可以用高阶函数非常方便地实现命令模式,命令模式在 JavaScript 语言中是一种隐形的模式。
系列链接
- JavaScript 设计模式(上)——基础知识
- JavaScript 设计模式(中)——1.单例模式
- JavaScript 设计模式(中)——2.策略模式
- JavaScript 设计模式(中)——3.代理模式
- JavaScript 设计模式(中)——4.迭代器模式
- JavaScript 设计模式(中)——5.发布订阅模式
- JavaScript 设计模式(中)——6.命令模式
- JavaScript 设计模式(中)——7.组合模式
- JavaScript 设计模式(中)——8.模板方法模式
- JavaScript 设计模式(中)——9.享元模式
- JavaScript 设计模式(中)——10.职责链模式
- JavaScript 设计模式(中)——11. 中介者模式
- JavaScript 设计模式(中)——12. 装饰者模式
- JavaScript 设计模式(中)——13.状态模式
- JavaScript 设计模式(中)——14.适配器模式
- JavaScript 设计模式(下)——设计原则
- JavaScript 设计模式练习代码
本文主要参考了《JavaScript设计模式和开发实践》一书