文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

面试官:说说你对命令模式的理解?应用场景?

2024-12-02 17:52

关注

一、是什么

命令模式是最简单和优雅的模式之一,命令模式中的命令指的是一个执行某些特定事情的指令

该模式旨在将函数的调用、请求和操作封装成为一个单一的对象

请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令

例如在一个快餐店,用户向服务员点餐。服务员将用户的需求记录在清单上:

二、实现

命令模式由三种角色构成:

 

 

实现代码如下:

  1. class Receiver {  // 接收者类 
  2.   execute() { 
  3.     console.log('接收者执行请求'); 
  4.   } 
  5.  
  6. class Command {   // 命令对象类 
  7.   constructor(receiver) { 
  8.     this.receiver = receiver; 
  9.   } 
  10.   execute () {    // 调用接收者对应接口执行 
  11.     console.log('命令对象->接收者->对应接口执行'); 
  12.     this.receiver.execute(); 
  13.   } 
  14.  
  15. class Invoker {   // 发布者类 
  16.   constructor(command) { 
  17.     this.command = command; 
  18.   } 
  19.   invoke() {      // 发布请求,调用命令对象 
  20.     console.log('发布者发布请求'); 
  21.     this.command.execute(); 
  22.   } 
  23.  
  24. const warehouse = new Receiver();       // 厨师 
  25. const order = new Command(warehouse);   // 订单 
  26. const client = new Invoker(order);      // 请求者 
  27. client.invoke(); 

三、应用场景

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

菜单

现在我们需要实现一个界面,包含很多个按钮。每个按钮有不同的功能,我们利用命令模式来完成

  1. "button1"
  2. "button2"
  3. "button3"
  4.  
  5.  

然后定义一个setCommand函数,负责将按钮安装命令,可以确定的是,点击按钮会执行某个 command 命令,执行命令的动作被约定为调用 command 对象的 execute() 方法。如下:

  1. var button1 = document.getElementById('button1'
  2. var setCommand = function(button, conmmand) { 
  3.   button.onclick = function() { 
  4.     conmmand.execute() 
  5.   } 

点击按钮之后具体行为包括刷新菜单界面、增加子菜单和删除子菜单等,这几个功能被分布在 MenuBar 和 SubMenu 这两个对象中:

  1. var MenuBar = { 
  2.   refresh: function() { 
  3.     console.log('刷新菜单目录'
  4.   } 
  5. var SubMenu = { 
  6.   addfunction() { 
  7.     console.log('增加子菜单'
  8.   }, 
  9.   del: function(){ 
  10.     console.log('删除子菜单'); 
  11.   } 

这些功能需要封装在对应的命令类中:

  1. // 刷新菜单目录命令类 
  2. class RefreshMenuBarCommand { 
  3.     constructor(receiver) { 
  4.         this.receiver = receiver; 
  5.     } 
  6.  
  7.     execute() { 
  8.         this.receiver.refresh(); 
  9.     } 
  10.  
  11. // 增加子菜单命令类 
  12. class AddSubMenuCommand { 
  13.     constructor(receiver) { 
  14.         this.receiver = receiver; 
  15.     } 
  16.  
  17.     execute() { 
  18.         this.receiver.refresh(); 
  19.     } 
  20.  
  21. // '删除子菜单命令类 
  22. class DelSubMenuCommand { 
  23.     constructor(receiver) { 
  24.         this.receiver = receiver; 
  25.     } 
  26.  
  27.     execute() { 
  28.         this.receiver.refresh(); 
  29.     } 

最后就是把命令接收者传入到 command 对象中,并且把 command 对象安装到 button 上面:

  1. var refreshMenuBarCommand = new RefreshMenuBarCommand(MenuBar); 
  2. var addSubMenuCommand = new AddSubMenuCommand(SubMenu); 
  3. var delSubMenuCommand = new DelSubMenuCommand(SubMenu); 
  4.  
  5. setCommand(button1, refreshMenuBarCommand); 
  6. setCommand(button2, addSubMenuCommand); 
  7. setCommand(button3, delSubMenuCommand); 

撤销

命令模式的作用不仅是封装运算块,而且可以很方便地给命令对象增加撤销操作

页面中有一个 input 文本框和一个 button 按钮,文本框中可以输入一些数字,表示小球移动后的水平位置,小球在用户点击按钮后立刻开始移动,如下:

  1.   id="ball" 
  2.   style="position: absolute; background: #000; width: 50px; height: 50px" 
  3. >
 
  • 输入小球移动后的位置:"pos" /> 
  • "moveBtn">开始移动 
  •  
  • 换成命令模式如下:

    1. var ball = document.getElementById("ball"); 
    2. var pos = document.getElementById("pos"); 
    3. var moveBtn = document.getElementById("moveBtn"); 
    4. var MoveCommand = function (receiver, pos) { 
    5.   this.receiver = receiver; 
    6.   this.pos = pos; 
    7. }; 
    8. MoveCommand.prototype.execute = function () { 
    9.   this.receiver.start("left", this.pos, 1000, "strongEaseOut"); 
    10. }; 
    11. var moveCommand; 
    12. moveBtn.onclick = function () { 
    13.   var animate = new Animate(ball); 
    14.   moveCommand = new MoveCommand(animate, pos.value); 
    15.   moveCommand.execute(); 
    16. }; 

    撤销操作的实现一般是给命令对象增加一个名为 unexecude 或者 undo的方法,在该方法里执行 execute 的反向操作

    在 command.execute 方法让小球开始真正运动之前,需要先记录小球的当前位置,在 unexecude 或者 undo 操作中,再让小球回到刚刚记录下的位置,代码如下:

    1. class MoveCommand { 
    2.     constructor(receiver, pos) { 
    3.         this.receiver = receiver; 
    4.         this.pos = pos; 
    5.         this.oldPos = null
    6.     } 
    7.  
    8.     execute() { 
    9.         this.receiver.start('left', this.pos, 1000, 'strongEaseOut'); 
    10.         this.oldPos = this.receiver.dom.getBoundingClientRect()[this.receiver.propertyName];  // 记录小球开始移动前的位置 
    11.     } 
    12.  
    13.     undo() { 
    14.         this.receiver.start('left', this.oldPos, 1000, 'strongEaseOut'); // 回到小球移动前记录的位置 
    15.     } 
    16.  
    17. var moveCommand; 
    18. moveBtn.onclick = function () { 
    19.   var animate = new Animate(ball); 
    20.   moveCommand = new MoveCommand(animate, pos.value); moveCommand.execute(); 
    21. }; 
    22. cancelBtn.onclick = function () { 
    23.   moveCommand.undo();// 撤销命令 
    24. }; 

    现在通过命令模式轻松地实现了撤销功能。如果用普通方法调用来实现,也许需要每次都手工记录小球的运动轨迹,才能让它还原到之前的位置

    而命令模式中小球的原始位置在小球开始移动前已经作为 command 对象的属性被保存起来,所以只需要再提供一个 undo 方法,并且在 undo方法中让小球会到刚刚记录的原始位置就可以

    参考文献

    https://www.runoob.com/design-pattern/command-pattern.html

    https://juejin.cn/post/6844903673697402888

    https://juejin.cn/post/6995474681813811208

     

    来源:JS每日一题内容投诉

    免责声明:

    ① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

    ② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

    软考中级精品资料免费领

    • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

      难度     813人已做
      查看
    • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

      难度     354人已做
      查看
    • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

      难度     318人已做
      查看
    • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

      难度     435人已做
      查看
    • 2024年上半年系统架构设计师考试综合知识真题

      难度     224人已做
      查看

    相关文章

    发现更多好内容

    猜你喜欢

    AI推送时光机
    位置:首页-资讯-后端开发
    咦!没有更多了?去看看其它编程学习网 内容吧
    首页课程
    资料下载
    问答资讯