JavaScript 设计模式(中)——6.命令模式

6 命令模式

6.1 命令模式的用途

命令模式的命令指的是一个执行某些特定事情的指令;

命令模式的应用场景:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么。此时希望用一种松耦合的方式来设计程序,使得请求发图送者和请求接收者能够消除彼此之间的耦合关系;

6.2 命令模式的例子——菜单程序

实现一个点击不同按钮调用不同方法的功能 (模拟传统面向对象语言的命令模式实现):

  1. 按钮绘制:

  
  
  


  1. 定义 setCommand 函数, setCommand 函数负责往按钮上面安装命令。约定点击按钮会执行某个 command 命令,执行命令的动作被约定为调用 command 对象的 execute() 方法;
var setCommand = function( button, command ){
  button.onclick = function(){ command.execute(); }
};
  1. 编写点击按钮之后的具体行为:
var MenuBar = {
  refresh: function(){ console.log( '刷新菜单目录' ); }
};
var SubMenu = {
  add: function(){ console.log( '增加子菜单' ); },
  del: function(){ console.log( '删除子菜单' ); }
};
  1. 封装行为在命令类中:
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( '删除子菜单' ); };
  1. 把命令接收者传入到 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 语言中是一种隐形的模式。

系列链接

  1. JavaScript 设计模式(上)——基础知识
  2. JavaScript 设计模式(中)——1.单例模式
  3. JavaScript 设计模式(中)——2.策略模式
  4. JavaScript 设计模式(中)——3.代理模式
  5. JavaScript 设计模式(中)——4.迭代器模式
  6. JavaScript 设计模式(中)——5.发布订阅模式
  7. JavaScript 设计模式(中)——6.命令模式
  8. JavaScript 设计模式(中)——7.组合模式
  9. JavaScript 设计模式(中)——8.模板方法模式
  10. JavaScript 设计模式(中)——9.享元模式
  11. JavaScript 设计模式(中)——10.职责链模式
  12. JavaScript 设计模式(中)——11. 中介者模式
  13. JavaScript 设计模式(中)——12. 装饰者模式
  14. JavaScript 设计模式(中)——13.状态模式
  15. JavaScript 设计模式(中)——14.适配器模式
  16. JavaScript 设计模式(下)——设计原则
  17. JavaScript 设计模式练习代码

本文主要参考了《JavaScript设计模式和开发实践》一书

你可能感兴趣的:(JavaScript 设计模式(中)——6.命令模式)