Javascript命令模式

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.mzph.cn/news/114471.shtml

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

相关文章

C++STL的迭代器(iterator)

一、定义 迭代器是一种检查容器内元素并且遍历容器内元素的数据类型。 【引用自&#xff1a;C迭代器&#xff08;iterator&#xff09;_c iterator_NiUoW的博客-CSDN博客】迭代器是一个变量&#xff0c;相当于容器和操纵容器的算法之间的中介。C更趋向于使用迭代器而不是数组下…

makesense在线yolov5标注

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

在软件测试行业这种情况下,凭什么他能拿25k?我却约面试都难?

在当今竞争激烈的软件测试行业中&#xff0c;近期的招聘市场确实面临一些挑战。大量的求职者争相涌入岗位&#xff0c;许多热衷于功能测试的人士甚至难以找到理想的工作机会。更不幸的是&#xff0c;连自动化测试和性能测试这些专业领域也受到了测试开发人员的竞争压力。然而&a…

整理MongoDB文档:身份验证

整理MongoDB文档:身份验证 个人博客&#xff0c;求关注。 文章概叙 本文主要讲MongoDB在单机状态下的账户配置。理解了MongoDB的语法&#xff0c;对于如何配置用户权限会知道怎么配置&#xff0c;但是请注意给谁配置什么权限才是最重要的。 最小权限原则 系统的每个程序或者…

kubernetes(2)

pod管理 应用部署 上传测试镜像 [rootk8s1 docker]# docker push reg.westos.org/library/myapp:v1 [rootk8s1 docker]# docker push reg.westos.org/library/myapp:v2创建自助式pod&#xff08;生产不推荐&#xff09; [rootk8s2 ~]# kubectl run demo --imagemyapp:v1[ro…

uniapp 安装 u-view 组件库

u-view 组件库安装教程&#xff1a;https://uviewui.com/components/install.html 注&#xff1a;以下使用 HBuilderx 安装 u-view 2.0 版本&#xff0c;不适用于其它版本。 1.安装 u-view 组件库 2、注册并登录 HBuilderx 账号&#xff0c;点击下载 u-view 组件库。 3、点击…

如何开发一个 Safari 插件

本文字数&#xff1a;2493字 预计阅读时间&#xff1a;15分钟 由于常用浏览器是Safari&#xff0c;而Safari浏览器的插件比不上Chrome&#xff0c;所以就有了自己开发常用的Safari插件的想法。 打算开发当前页面生成二维码的Extension&#xff0c;因为网络原因&#xff0c;AirD…

真实感受:是智能家居在选择合适的技术!

科技从来都是为了让我们的生活更加的简单、舒适&#xff0c;而智能家居的智能&#xff0c;体现在如何更更更方便的使用我需要控制的家居。 例如&#xff1a;下班躺在床上想休息&#xff0c;房间和大厅的灯还开着&#xff0c;这时你会选择什么产品躺着解决问题&#xff1f; 红外…

Hadoop3教程(二十八):(生产调优篇)NN、DN的多目录配置及磁盘间数据均衡

文章目录 &#xff08;148&#xff09;NN多目录配置&#xff08;149&#xff09;DataNode多目录配置及磁盘间数据平衡磁盘间数据均衡 参考文献 &#xff08;148&#xff09;NN多目录配置 NN多目录的意思是&#xff0c;本地目录可以配置成多个&#xff0c;且每个目录存放内容相…

20款VS Code实用插件推荐

前言&#xff1a; VS Code是一个轻量级但功能强大的源代码编辑器&#xff0c;轻量级指的是下载下来的VS Code其实就是一个简单的编辑器&#xff0c;强大指的是支持多种语言的环境插件拓展&#xff0c;也正是因为这种支持插件式安装环境开发让VS Code成为了开发语言工具中的霸主…

【数据结构】八大排序

目录 1. 排序的概念及其作用 1.1 排序的概念 1.2 排序运用 1.3 常见的排序算法 2. 常见排序算法的实现 2.1 插入排序 2.1.1 基本思想 2.1.2 直接插入排序 2.1.3 希尔排序&#xff08;缩小增量排序&#xff09; 2.2 选择排序 2.2.1 基本思想 2.2.2 直接选择排序 2.2…

Linux下Samba服务安装及启用全攻略

Linux下Samba服务安装及启用全攻略 前言一、安装SSH Server二、安装Samba Server1.安装net-tool2.建立账号的samba3.windows通过Samba与linux共享文件4.使用远程工具登录Linux 总结 前言 提示&#xff1a;本文详解了在Linux系统下如何安装和启用Samba服务&#xff0c;涵盖了从…

【2023年11月第四版教材】软考高项极限冲刺篇笔记(2)

1 我们要知道的事 1、考试的选择题不会出假大空的管理,一般较为具体 2.3 信息系统治理 首先治理的目标是什么 治理的管理层分为三层 原则:简单透明适合 COBIT IT审计范围:总体、组织、物理、逻辑、其他 IT审计风险:固有、控制、检查、总体审计 IT审计方法:访谈、调查、…

【微服务 SpringCloudAlibaba】实用篇 · Nacos注册中心

微服务&#xff08;5&#xff09; 文章目录 微服务&#xff08;5&#xff09;1. 认识和安装Nacos2. 服务注册到nacos和拉取服务1&#xff09;引入依赖2&#xff09;配置nacos地址3&#xff09;重启 3. 服务分级存储模型3.1 给user-service配置集群3.2 同集群优先的负载均衡 4. …

vue el-dialog弹出框自定义指令实现拖拽改变位置-宽度-高度

前言 在实际开发中我们经常使用el-dialog弹出框做表单&#xff0c;一般情况都是居中。遮挡到了一部分数据 当我们想要查看弹出框下面的数据时&#xff0c;就只能先把弹出框关闭&#xff0c;查看完数据之后在打开弹框 我们通过动态样式&#xff0c;和鼠标事件就可以实现。但自…

effective c++学习笔记(后四章)

六 继承与面向对象设计 红色字 \color{FF0000}{红色字} 红色字 32 确定你的public继承塑模出 is-a关系 如果你令class D (“Derived”)以public形式继承class B (“Base”)&#xff0c;你便是告诉C编译器&#xff08;以及你的代码读者&#xff09;说&#xff0c;每一个类型为…

Origami Studio for Mac:塑造未来,掌握原型设计之巅

在当今高度竞争的设计领域&#xff0c;原型设计的重要性不言而喻。它不仅是沟通想法&#xff0c;也是测试和改进设计的关键环节。而现在&#xff0c;一款强大的原型设计工具——Origami Studio for Mac&#xff0c;正在席卷设计界&#xff0c;以其独特的功能和卓越的性能&#…

shell变量

shell变量之学习笔记 Shell变量概念1 shell变量分类&#xff1a;2 变量的赋值3 变量赋值格式&#xff1a;4 变量命名方式5 变量声明6 变量引用7 变量清除8 变量只读9 内部参数变量10 位置参数变量11 退出和返回状态12 命令替换13 read命令14 字符串长度与截取15 字符串替换16 变…

Spring Security—Servlet 应用架构

目录 一、Filter&#xff08;过滤器&#xff09;回顾 二、DelegatingFilterProxy 三、FilterChainProxy 四、SecurityFilterChain 五、Security Filter 六、打印出 Security Filter 七、添加自定义 Filter 到 Filter Chain 八、处理 Security 异常 九、保存认证之间的…

C语言【文件】

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