命令模式是最简单和优雅的模式之一,命令模式中的命令(command)是指一个执行某些特定事情的指令。
应用场景:有时候需要向某些对象发送请求,但是并不知道请求的接受者是谁,也不知道被请求的操作是什么,此时希望用一种
松解耦的方式来设计软件,使得请求发送者和请求接受者能够消除彼此之间的耦合关系。
一个简单JavaScript例子:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>命令模式</title> 6 <style> 7 body{ 8 padding: 0; 9 margin: 0; 10 } 11 .ball{ 12 position: absolute; 13 background: #000; 14 width: 50px; 15 height: 50px; 16 top: 200px; 17 } 18 </style> 19 </head> 20 <body> 21 <div id="ball" class="ball"></div> 22 输入小球移动后的位置:<input type="text" id="pos"> 23 <button id="moveBtn">开始移动</button> 24 <button id="cancelBtn">cancel</button> 25 <script> 26 27 // 营运策略模式封装一系列缓动算法 28 // t:已消耗的时间 b:小球的原始位置 c:小球的目标位置 d:动画持续的总时间 29 // 返回当前位置 30 var tween = { 31 linear: function(t, b, c, d) { 32 return c*t/d + b; 33 }, 34 easeIn: function(t, b, c, d) { 35 return c * (t /= d) * t + b; 36 }, 37 strongEaseIn: function(t, b, c, d) { 38 return c * (t /= d) * t * t * t + b; 39 }, 40 strongEaseOut: function(t, b, c, d) { 41 return c * (( t = t / d - 1 ) * t * t * t * t + 1) + b; 42 }, 43 sineaseIn: function(t, b, c, d) { 44 return c * (t /= d) * t * t + b; 45 }, 46 sineaseOut: function(t, b, c, d) { 47 return c * (( t = t / d - 1) * t * t + 1) + b; 48 } 49 }; 50 51 // 定义动画类 52 var Animate = function(dom) { 53 this.dom = dom; 54 this.startTime = 0; 55 this.startPos = 0; 56 this.endPos = 0; 57 this.propertyName = null; 58 this.easing = null; 59 this.duration = null; 60 } 61 62 // 动画启动 63 Animate.prototype.start = function(propertyName, endPos, duration, easing) { 64 this.propertyName = propertyName; 65 this.startTime = +new Date(); 66 this.startPos = this.dom.getBoundingClientRect()[propertyName]; 67 this.endPos = endPos; 68 this.duration = duration; 69 this.easing = tween[easing]; 70 71 var self = this; 72 var timeId = setInterval(function() { 73 if (self.step() === false) { 74 clearInterval(timeId); 75 } 76 }, 1000/60) 77 } 78 79 Animate.prototype.step = function() { 80 var t = +new Date(); 81 if (t > this.startTime + this.duration) { 82 this.update(this.endPos); 83 return false; 84 } 85 var pos = this.easing(t - this.startTime, 86 this.startPos, this.endPos - this.startPos, this.duration); 87 this.update(pos); 88 } 89 90 Animate.prototype.update = function( pos ) { 91 this.dom.style[ this.propertyName ] = pos + 'px'; 92 } 93 94 95 var ball = document.getElementById('ball'); 96 var pos = document.getElementById('pos'); 97 var moveBtn = document.getElementById('moveBtn'); 98 var cancelBtn = document.getElementById('cancelBtn'); 99 100 // 使用命令模式实现事件和dom的解耦 101 var MoveCommand = function(receiver, pos) { 102 this.receiver = receiver; 103 this.pos = pos; 104 this.oldPos = null; 105 } 106 107 MoveCommand.prototype.excute = function() { 108 this.receiver.start('left', this.pos, 1000, 'strongEaseOut'); 109 this.oldPos = this.receiver.dom.getBoundingClientRect()[this.receiver.propertyName]; 110 } 111 112 MoveCommand.prototype.undo = function() { 113 this.receiver.start('left', this.oldPos, 1000, 'strongEaseOut'); 114 } 115 116 var moveCommand; 117 118 moveBtn.onclick = function() { 119 var animate = new Animate( ball ); 120 moveCommand = new MoveCommand(animate, pos.value); 121 moveCommand.excute(); 122 } 123 124 cancelBtn.onclick = function() { 125 console.log(moveCommand); 126 moveCommand.undo(); 127 } 128 </script> 129 </body> 130 </html>