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

6 命令模式

6.1 命令模式的用途

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

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

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

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

  1. 按钮绘制:
<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' );
</script>
  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设计模式和开发实践》一书

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容