命令模式是最简单的模式之一,命令模式中的命令command指的是一个执行某些特定事情的指令。命令模式最常见的应用场景是:有时候需要向某些对象发送请求,但是并不知道请求的接受者是谁,此时希望用一种松耦合的方式来设计程序,使得请求发送者和请求接收者能够消除彼此之间的耦合关系。
详细demo请查看command.js
菜单程序
假设这样一种场景,用户界面上有数十个button按钮,因为项目复杂,我们让A负责按钮的UI,B负责点击按钮后的具体行为,这些行为都将被封装在对象内。
<body>
<button id="button1">1</button>
<button id="button2">2</button>
<button id="button3">3</button>
</body>
<script>
var btn1 = document.getElementById('button1');
var btn2 = document.getElementById('button2');
var btn3 = document.getElementById('button3');
</script>
我们在这里可以找到使用命令模式的理由,点击了按钮之后,必须向某些负责具体行为的对象发送请求,但是对A来说,目前不知道接收者是什么对象,也不知道接收者究竟会做什么。设计模式的主题总是把不变的和变化的事物分离开来,以便揭开按钮和具体负责行为对象之间的耦合。
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('删除子菜单');
}
}
在让button变得有用起来之前,我们要先把这些行为都封装在命令类中
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();
}
最后就是把命令接收者传入到command对象中,并且把command对象安装在button上面
var refreshMenuBarCommand = new RefreshMenuBarCommand(MenuBar);
var addSubMenuCommand = new AddSubMenuCommand(SubMenu);
var delSubMenuCommand = new DelSubMenuCommand(SubMenu);
setCommand(btn1, refreshMenuBarCommand);
setCommand(btn2, addSubMenuCommand);
setCommand(btn3, delSubMenuCommand);
js中的命令模式
也许我们会感到很奇怪,所谓的命令模式,看起来就是给对象的某个方法取了execute的名字,引入command对象和receiver对象把简单的事情复杂化了。命令模式的由来,其实是回调函数的一个面向对象的替代品。
在js中,函数作为一等对象,本身就可以被当作参数传递
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(btn1, refreshMenuBarCommand);
当然,除了执行命令之外,将来还可能提供撤销命令等操作,那我们最好把执行函数改为调用execute方法
var RefreshMenuBarCommand = function(receiver){
return {
execute: function(){
receiver.refresh();
}
}
}
var setCommand = function(button, command){
button.onclick = function(){
command.execute();
}
}