本文由我们组内杨俊宁总结分享
一, 什么是命令模式
假设有一个快餐店, 而我是该餐厅的点餐服务员, 每天当客人点餐或者打来订餐电话后, 我会把他的需求都写在清单上, 然后交给厨房, 客人不用关心是哪些厨师帮他炒菜. 这些记录着订餐信息的清单, 便是命令模式的命令对象.
二, 命令模式的用途
命令模式最常用的应用场景是: 有时候需要向某些对象发送请求, 但是并不知道请求的接收者是谁, 也不知道被请求的操作是什么. 此时希望用一种松耦合的方式来设计程序, 使得请求发送者和请求接收者能够消除彼此的耦合关系.
就订餐而言, 客人需要向厨师发送请求, 但是完全不知道这些厨师的名字与联系方式.
因此命令模式把请求封装成 command 对象, 这个对象可以在程序中四处传递, 解开了调用者与接收者之间的耦合关系.
三, 实例 - 菜单程序
假设我们在编写一个用户界面程序, 上面有很多 button 按钮, 我们需要绘制按钮并且绑定事件. 让 A 程序员绘制按钮, 让 B 程序员绑定事件, 这是很常见的分工. 参加代码:
- <body>
- <button></button>
- <button></button>
- <button></button>
- <button></button>
- <button></button>
- <script type="text/javascript">
- var setCommand = function (button,func) {
- button.onclick = function () {
- func()
- }
- }
- var MenuBar = {
- refresh : function () {
- console.log('refresh menu pages')
- }
- }
- var RefreshMenuBarCommand = function (receiver) {
- return function () {
- receiver.refresh()
- }
- }
- var RefreshMenuBarCommand = RefreshMenuBarCommand(MenuBar)
- setCommand(button1,RefreshMenuBarCommand)
- </script>
- </body>
以上就简单完成一个命令模式的实例, 下面来看看怎么撤销命令.
- <body>
- <div id="ball" style="position: absolute;background: #000;width: 50px;height: 50px"></div>
- 输入小球移动后的位置: <input id="pos" />
- <button id="moveBtn"> 开始移动 </button>
- <script type="text/javascript">
- var ball = document.getElementById('ball'),
- pos = document.getElementById('pos'),
- 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')
- };
- 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();
- }
- var Animate = function (dom) {
- this.dom = dom;
- this.startTime = 0;
- this.startPos = 0;
- this.endPos = 0;
- this.propertyName = null;
- this.easing = null;
- this.duration = null;
- }
- Animate.prototype.start = function (propertyName,endPos,duration,easing) {
- this.startTime = +new Date;
- this.startPos = this.dom.getBoundingClientRect()[propertyName];
- this.propertyName = propertyName;
- this.endPos = endPos;
- this.duration = duration;
- this.easing = tween[easing];
- var self = this;
- var timeId = setInterval(function () {
- if(self.step() === false){
- clearInterval(timeId);
- }
- },19)
- };
- Animate.prototype.step = function () {
- var t = +new Date;
- if(t>= this.startTime + this.duration){
- this.update(this.endPos)
- return false;
- }
- var pos = this.easing(t - this.startTime , this.startPos,
- this.endPos - this.startPos , this.duration)
- this.update(pos)
- }
- Animate.prototype.update = function (pos) {
- this.dom.style[this.propertyName] = pos + 'px';
- }
- </script>
- </body>
四, 宏命令
宏命令是一组命令的集合, 通过执行宏命令的方式可以批量执行命令, 想象一下, 有一个万能遥控器, 每天回家的时候, 只要按一下就会帮我们关上房间门打开电脑登录 qq. 下面我们实现一个宏命令.
- var closeDoorCommand = {
- execute:function () {
- console.log('closedoor')
- }
- };
- var openPcCommand = function () {
- execute : function () {
- console.log('open PC')
- }
- };
- var openQQCommand = {
- execute : function () {
- console.log('login 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()
五, ES6 实现命令模式
- 'use strict';
- // 激活类
- class Invoker {
- constructor() {
- console.log('Invoker Class created');
- }
- storeCommand(command) {
- this.command = command;
- console.log('Invoker.storeCommand invoked');
- }
- }
- // 命令类
- class Command {
- constructor() {
- console.log('Command Class created');
- }
- execute() {
- console.log('Command.execute invoked');
- }
- }
- class ConcreteCommand extends Command {
- constructor(receiver, state) {
- super();
- this.receiver = receiver;
- console.log('ConcreteCommand Class created');
- }
- execute() {
- console.log('ConcreteCommand.execute invoked');
- this.receiver.action();
- }
- }
- class Receiver {
- constructor() {
- console.log('Receiver Class created');
- }
- action() {
- console.log('Receiver.action invoked');
- }
- }
- var invoker = new Invoker();
- var receiver = new Receiver();
- var command = new ConcreteCommand(receiver);
- invoker.storeCommand(command);
- invoker.command.execute();
六, 小结
敏捷开发原则告诉我们, 不要为代码添加基于猜测的, 实际不需要的功能, 如果不清楚一个系统是否需要命令模式, 一般就不要着急去实现它, 事实上, 在需求的时通过重构实现这个模式并不困难, 只有在真正需求如撤销, 恢复操作等功能时, 把原来的代码重构为命令模式才有意义.
来源: https://juejin.im/entry/5b8c7a606fb9a019d20e2bba