JavaScript设计模式———命令模式
命令模式最常见的应用场景是:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么。此时希望用一种松耦合的方式来设计程序,使得请求发送者和请求接收者能够消除彼此之间的耦合关系。
命令模式会把请求封装为一个 command 对象,利用 command 对象去调用实际接收者,从而达到发送者和请求接收者松耦合的目的。
命令模式与策略模式有些类似, 在 JavaScript 中它们都是隐式的。都是将请求委托给相应的执行对象,不同的是命令模式大都引有接收者的引用,策略模式是直接返回相应操作。
基于面向对象的命令模式
例子
多个按钮,每个按钮执行不同操作,将不同操作交予不同command 对象:
<body>
<button id="button1">点击按钮 1</button>
<button id="button2">点击按钮 2</button>
<button id="button3">点击按钮 3</button>
</body>
<script>
var button1 = document.getElementById( 'button1' ),
var button2 = document.getElementById( 'button2' ),
var button3 = document.getElementById( 'button3' );
// 注册命令
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('删除子菜单');
};
var refreshMenuBarCommand = new RefreshMenuBarCommand(MenuBar);
var addSubMenuCommand = new AddSubMenuCommand(SubMenu);
var delSubMenuCommand = new DelSubMenuCommand(SubMenu);
setCommand(button1, refreshMenuBarCommand);
setCommand(button2, addSubMenuCommand);
setCommand(button3, delSubMenuCommand);
</script>
JavaScript 中的命令模式
命令模式将过程式的请求调用封装在 command 对象的 execute 方法里,通过封装方法调用,我们可以把运算块包装成形。 command 对象可以被四处传递,所以在调用命令的时候,客户( Client)不需要关心事情是如何进行的。
JavaScript 作为将函数作为一等对象的语言,**跟策略模式一样,命令模式也早已融入到了JavaScript 语言之中。**运算块不一定要封装在 command.execute 方法中,也可以封装在普通函数中。函数作为一等对象,本身就可以被四处传递。即使我们依然需要请求“接收者”,那也未必使用面向对象的方式,闭包可以完成同样的功能。
面向对象设计中,命令模式的接收者被当成 command 对象的属性保存起来,同时约定执行命令的操作调用 command.execute 方法。在使用闭包的命令模式实现中,接收者被封闭在闭包产生的环境中,执行命令的操作可以更加简单,仅仅执行回调函数即可。无论接收者被保存为对象的属性,还是被封闭在闭包产生的环境中,在将来执行命令的时候,接收者都能被顺利访问。
var setCommand = function(button, func) {
button.onclick = function() {
func();
}
};
var MenuBar = {
refresh: function() {
console.log('刷新菜单界面');
}
};
var RefreshMenuBarCommand = function(receiver) {
return function() {
receiver.refresh();
}
};
var refreshMenuBarCommand = RefreshMenuBarCommand(MenuBar);
setCommand(button1, refreshMenuBarCommand);
智能命令与傻瓜命令
一般来说,命令模式都会在 command 对象中保存一个接收者来负责真正执行客户的请求,这种情况下命令对象是“傻瓜式”的,它只负责把客户的请求转交给接收者来执行,这种模式的好处是请求发起者和请求接收者之间尽可能地得到了解耦。
但是我们也可以定义一些更“聪明”的命令对象,“聪明”的命令对象可以直接实现请求,这样一来就不再需要接收者的存在,这种“聪明”的命令对象也叫作智能命令。没有接收者的智能命令,退化到和策略模式非常相近,从代码结构上已经无法分辨它们,能分辨的只有它们意图的不同。策略模式指向的问题域更小,所有策略对象的目标总是一致的,它们只是达到这个目标的不同手段,它们的内部实现是针对“算法” 而言的。 而智能命令模式指向的问题域更广, command对象解决的目标更具发散性。命令模式还可以完成撤销、排队等功能。