Javascript命令模式

news2024/11/16 21:53:20

Javascript命令模式

  • 1 什么是命令模式
  • 2 命令模式的例子—菜单程序
  • 3 JavaScript 中的命令模式
  • 4 撤销命令
  • 5 宏命令

1 什么是命令模式

在一个餐厅中,当客人现场点餐或者打电话订餐时,老板会把客人的需求写在清单上,厨师会按照清单的顺序给客人炒菜,有时餐厅还可以满足定时点餐,比如x时间之后再上菜,如果客人需要撤销订单,可以直接打电话给餐厅。

这些记录着订餐信息的清单,就是命令模式中的命令对象

命令模式允许将功能请求封装为独立的对象,并根据需要将其排队、记录、撤销或重做。该模式的核心是将请求与其调用者分离,从而使命令对象独立于发送者和接收者。

在命令模式中,有4个主要角色:命令对象、客户端、接收者和调用者。命令对象封装了一个特定的功能请求,而客户端创建命令对象并将其传递给调用者。调用者是负责执行命令的对象,并将其发送给接收者执行所需的操作。

命令模式在Javascript中有很多应用场景,例如撤销和重做功能、动态添加命令、撤销命令、实现编辑器、处理用户交互等。

对于订餐来说,客人需要向厨师发送请求,但是并不关心厨师的名字和联系方式,也不知道厨师炒菜的方式和步骤。命令模式把客人订餐的请求封装成command对象,这个对象可以在程序中被四处传递,就像订单可以从服务员手中传到厨师的手中。这样一来,客人不需要知道厨师的名字,从而解开了请求调用者和请求接收者之间的耦合关系。

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

假设我们封装一个按钮组的组件,每个按钮都有自己的click事件,对于封装这个组件的人来说,并不关心这个按钮点击之后接收者是什么对象,也不知道接收者究竟会做什么,这时我们可以借助命令对象的帮助,以便解开按钮和负责具体行为对象之间的耦合,首先是按钮组绘制:

<button id="button1">点击按钮1</button>
<button id="button2">点击按钮2</button>
<button id="button3">点击按钮3</button>
<script>
  var button1 = document.getElementById("button1");
  var button2 = document.getElementById("button2");
  var button3 = document.getElementById("button3");
</script>

接下来定义setCommand函数,setCommand函数负责往按钮上面安装命令。可以肯定的是,点击按钮会执行某个command命令,执行命令的动作被约定为调用command对象的execute()方法。

var setCommand = function (button, command) {
  button.onclick = function () {
    command.execute();
  };
};

最后是按钮的点击事件,点击按钮后分别由刷新菜单界面、增加子菜单和删除子菜单这几个功能,这几个功能被分布在MenuBarSubMenu这两个对象中:

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 () {
  this.receiver.del();
};

最后把命令接收者传入到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);

在这里插入图片描述
以上只是一个很简单的命令模式示例,但从中可以看到我们是如何把请求发送者和请求接收
者解耦开的。

3 JavaScript 中的命令模式

也许我们会感到很奇怪,所谓的命令模式,看起来就是给对象的某个方法取了execute的名字。引入command对象和receiver这两个无中生有的角色无非是把简单的事情复杂化了,即使不用什么模式,用下面寥寥几行代码就可以实现相同的功能:

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);

这种说法是正确的,上面的示例代码是模拟传统面向对象语言的命令模式实现。命令模式将过程式的请求调用封装在command对象的execute方法里,通过封装方法调用,我们可以把运算块包装成形,command对象可以被四处传递,所以在调用命令的时候,客户不需要关心事情是如何进行的。

跟策略模式一样,命令模式也早已融入到了JavaScript语言之中。运算块不一定要封装在command.execute方法中,也可以封装在普通函数中。函数作为一等对象,本身就可以被四处传递。即使我们依然需要请求“接收者”,那也未必使用面向对象的方式,闭包可以完成同样的功能。

4 撤销命令

下面利用策略模式中Animate类编写一个动画,Animate类指路:JavaScript策略模式

这个动画的表现是让页面上的正方形移动到水平方向的某个位置。现在页面中有一个input文本框和一个button按钮,文本框中可以输入一些数字,表示正方形移动后的水平位置,正方形在用户点击按钮后立刻开始移动,代码如下:

<div id="ball" class="ball"></div>
请输入移动后的位置:<input id="pos" />
<button id="moveBtn">开始移动</button>
<script>
  var ball = document.getElementById("ball");
  var pos = document.getElementById("pos");
  var moveBtn = document.getElementById("moveBtn");
  moveBtn.onclick = function () {
    var animate = new Animate(ball);
    animate.start("left", pos.value, 1000, "strongEaseOut");
  };
</script>
.ball {
  position: absolute;
  background-color: pink;
  width: 50px;
  height: 50px;
}

在这里插入图片描述
如果文本框输入200,然后点击moveBtn按钮,可以看到正方形顺利地移动到水平方向200px的位置。现在我们需要一个方法让它还原到开始移动之前的位置,在页面上设计一个撤销按钮,点击撤销按钮之后,小球便能回到上一次的位置。

在给页面中增加撤销按钮之前,先把目前的代码改为用命令模式实现:

var ball = document.getElementById("ball");
var pos = document.getElementById("pos");
var moveBtn = document.getElementById("moveBtn");

var MoveCommand = function (receiver, pos) {
  this.receiver = receiver;
  this.pos = pos;
};
MoveCommand.prototype.execute = function () {
  this.receiver.start("left", this.pos, 1000, "strongEaseOut");
};

var moveCommand;
moveBtn.onclick = function () {
  var animate = new Animate(ball);
  moveCommand = new MoveCommand(animate, pos.value);
  moveCommand.execute();
};

接下来增加撤销按钮:

<div id="ball" class="ball"></div>
请输入移动后的位置:<input id="pos" />
<button id="moveBtn">开始移动</button>
<button id="cancelBtn">撤销</button>

撤销操作的实现一般是给命令对象增加一个名为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(); // 撤销命令
};

在这里插入图片描述
现在通过命令模式轻松地实现了撤销功能。如果用普通的方法调用来实现,也许需要每次都手工记录小球的运动轨迹,才能让它还原到之前的位置。而命令模式中小球的原始位置在小球开始移动前已经作为command对象的属性被保存起来,所以只需要再提供一个undo方法,并且在undo方法中让小球回到刚刚记录的原始位置就可以了。

5 宏命令

宏命令是一组命令的集合,通过执行宏命令的方式,可以一次执行一批命令。

比如说,家里有一个万能遥控器,每天回家的时候,只要按一个特别的按钮,它就会帮我们关上房间门,顺便打开电脑并登录游戏。

下面我们逐步创建一个宏命令,首先,我们依然要创建好各种Command

var closeDoorCommand = {
  execute: function () {
    console.log("关门");
  },
};
var openPcCommand = {
  execute: function () {
    console.log("开电脑");
  },
};
var openGameCommand = {
  execute: function () {
    console.log("打开游戏");
  },
};

接下来定义宏命令MacroCommandmacroCommand.add方法表示把子命令添加进宏命令对象,当调用宏命令对象的execute方法时,会迭代这一组子命令对象,并且依次执行它们的execute方法:

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();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1121523.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

qt 多语言版本 QLinguist使用方法

在使用qt开发一款软件时&#xff0c;可能需要考虑显示文本中英文等多语言版本。可以使用qt语言家的方式实现。 步骤&#xff1a; 1、代码中给控件设置文本时&#xff0c;记得带上QObject::tr() 2、工程pro文件中加入 TRANSLATIONS demo2_en.ts 3、Qt creator点击“工具”—“外…

C语言【文件】

目录 概念 文件名 文件的打开和关闭 fopen fclose 输入输出函数 fputc fgetc fputs fgets fprintf fscanf fwrite fread 三种流 scanf和sprintf 结构体转化 ​编辑 文件的随机读写 fseek ftell rewind 文本文件和二进制文件 文件读取结束的判定 文件缓冲…

【试题035】逻辑非!例题

1.题目&#xff1a;下列表达式值为1的是&#xff1f; A. 3 & 4 B. 2 >5 C. ! !8 D. 0 &#xff01; 0 2.分析&#xff1a; 关于A选项&#xff1a; 3的二进制是&#xff1a;0011 4的二进制是&#xff1a;0100 0011 & 0100 00000 关于B选项&#xff1a; …

双势阱模型

双势阱模型 原子钟 传统的原子钟利用氨分子 由于隧道效应&#xff0c;上顶点的氮原子可以贯穿三个氢原子形成的势垒&#xff0c;到达下顶点对体系注入微波能量后&#xff0c;氮原子在上下定点之间振荡&#xff0c;体系的能量在两个稳定态之间交替变换&#xff0c;其振荡频率决…

2023淘宝十一预售今天20点开启!有什么优惠活动我们来一起看一下!

2023双11预售玩法将于今天20点正式开启&#xff01;有神优惠活动和玩法攻略&#xff0c;我&#xff0c;们一起来看一下&#xff01; 今双十一预售有加购赢清空购物车大奖&#xff0c;攒幸运值领红包&#xff0c;限时抢免定金福利&#xff0c;做任务攒幸运值赢免定特权这几大活…

PS001:PS2020及GeographicImager6.2安装

引言&#xff1a;Geographic ImagerV6.2是一款专业的PS地理成像插件&#xff0c;通过安装这款插件可实现在PS中加载4G以上的.bigtiff格式影像并对其进行修改与保存。并且这款软件拥有投影信息修改、基于地理坐标进行影像裁切等多种功能。 一、插件介绍 Geographic ImagerV6.2是…

【Windows】Edge浏览器自动更新服务启用选禁用被拒绝访问的解决方案

Windows系统的服务窗口里&#xff0c;把一些服务的启动类型选择禁用有可能会提示拒绝访问&#xff0c;怎么弄呢&#xff0c;这里讲一讲怎样禁用这个服务。 举一个类似禁用服务的例子&#xff1a;怎样关闭Edge浏览器的自动更新服务&#xff0c; 关闭服务 已知&#xff0c;Win…

CesiumJS 中绘制大多边形

本文翻译自Cesium官方&#xff0c;有改动。 本文中提及到的“大多边形”就如下图所示。 在Cesium的早期版本和一些引擎中&#xff0c;我们绘制这种跨度比较大的多边形&#xff0c;经常会看到一些奇怪的冲突问题&#xff0c;如下图所示。 要渲染任何几何体&#xff0c;我们必…

makesense在线yolov5标注

文章目录 一、创建图片文件夹和label.txt二、在线标注数据 参考文章博主&#xff1a;风吹落叶花飘荡 一、创建图片文件夹和label.txt 创建一个放置图片的文件夹images&#xff0c;存放需要标注的图片&#xff08;图片最好重命名为1,2,3…避免后面混淆&#xff09; 创建label.t…

BadNets: Identifying Vulnerabilities in the Machine Learning Model Supply Chain

BadNets: Identifying Vulnerabilities in the Machine Learning Model Supply Chain----《BadNets:识别机器学习模型供应链中的漏洞》 背景&#xff1a; 许多用户将训练过程外包给云计算&#xff0c;或者依赖于经过训练的模型&#xff0c;然后根据特定的任务对模型进行微调。这…

patient feature-based softmax embedding

方法 作者未提供可直接运行的代码

JAVA基础(JAVA SE)学习笔记(七)面向对象编程(进阶)

前言 1. 学习视频&#xff1a; 尚硅谷Java零基础全套视频教程(宋红康2023版&#xff0c;java入门自学必备)_哔哩哔哩_bilibili 2023最新Java学习路线 - 哔哩哔哩 第二阶段&#xff1a;Java面向对象编程 6.面向对象编程&#xff08;基础&#xff09; 7.面向对象编程&…

Autosar代码阅读和调试方法

一、前言 众所周知Autosar工程代码量非常庞大&#xff0c;而且有非常多的宏定义&#xff0c;代码可读性非常不友好。但是目前国内外很多OEM和Tire1都是基于Autosar系统进行项目开发的。在开发过程中&#xff0c;出现一些BUG时必须去阅读和调试代码。这就要求开发人员具备很强代…

JavaScript基础知识16——分支语句

哈喽&#xff0c;大家好&#xff0c;我是雷工。 今天学习JavaScript基础知识的分支语句&#xff0c;以下为学习笔记。 1、程序三大流程控制语句 ○写几句就从上往下执行几句&#xff0c;这种叫做顺序结构&#xff1b; ○有时要根据条件选择执行代码&#xff0c;这种叫分支结构…

深度学习 | Pytorch深度学习实践 (Chapter 10、11 CNN)

十、CNN 卷积神经网络 基础篇 首先引入 —— 二维卷积&#xff1a;卷积层保留原空间信息关键&#xff1a;判断输入输出的维度大小特征提取&#xff1a;卷积层、下采样分类器&#xff1a;全连接 引例&#xff1a;RGB图像&#xff08;栅格图像&#xff09; 首先&#xff0c;老师…

2001-2022年全国290+个地级市高铁开通数据

2001-2022年全国290个地级市高铁开通数据 1、时间&#xff1a;2001-2022年 2、范围&#xff1a;298地级市&#xff08;293地级市数&#xff08;其中莱芜市2019年撤市设区&#xff09;4直辖市数 &#xff09; 3、来源&#xff1a;国家铁路局、铁路客货运输专刊及相关统计 国…

Homeassistant docker配置

Homeassistant docker配置 【说明】本系列为自用教程&#xff0c;记录以便下次使用 【背景】一台J1900 4G64G的小主机&#xff0c;安装了OP系统&#xff0c;里面自带了Docker。为实现Homeassistant&#xff08;简称HA&#xff09;控制智能家居设备&#xff0c;进行如下配置。 【…

使用 Data Assistant 快速创建测试数据集

使用 Data Assistant 快速创建测试数据集 Data Assistant 提供超过 100 种数据类型&#xff0c;为任何开发、测试或演示目的生成大量、异构、真实的数据。 官网地址&#xff1a; http://www.redisant.cn/da 主要功能 Windows 原生 Data Assistant 使用 Windows Native 技术…

Redis常见问题的解决方案(缓存穿透/缓存击穿/缓存雪崩/数据库缓存数据不一致)

Redis解决缓存数据库不一致的方案 用 先 操作数据库 再 操作缓存 的策略来实现缓存数据库数据一致具体做法是 更新数据库数据然后删除缓存 虽然还是会有线程安全问题 比如 假设此时缓存刚好失效了 线程1 查询缓存失败 从数据库读取了旧数据 还没写入缓存的时候 被调度到 线程…

C++标准模板(STL)- 类型支持 (运行时类型识别,type_info )

运行时类型识别 定义于头文件 <typeinfo> 含有某个类型的信息&#xff0c;由实现生成。​​这是 typeid 运算符所返回的类。 std::type_info 定义于头文件 <typeinfo> class type_info; 类 type_info 保有一个类型的实现指定信息&#xff0c;包括类型的名称和…