1.命令模式最常见的应用场景:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么,此时希望用一种松耦合的方式来设计程序,使得请求发送这和请求接收者能够消除彼此之间的耦合关系
举例:订餐,客人需要像厨师发送请求,但是完全不知道这些厨师的名字和联系方式,也不知道厨师的炒菜方式和步骤。命令模式把客人的请求封装成command对象,也就是订餐中的订单对象。这个对象可以在程序中被四处传递,就像订单可以从服务员手中传到厨师的手中,这样一来客人不需要知道厨师的名字,从而解开了请求调用者和请求接收者之间的耦合关系
代码实现(1.假设我们正在编写用户界面程序,一个程序员负责绘制按钮,而另外一些程序员则负责编写点击按钮后的具体行为,这些行为都被封装在对象里,那么当绘制完按钮,如何给它绑定事件呢)
<button id="btn">点击按钮</button>
<script>
var btn = document.getElementById( 'btn' );
{/* setCommand函数负责在按钮上面安装命令。可以肯定的是点击按钮会执行某个command命令,执行命令的动作被约定为调用command对象的execute方法 */}
var setCommand = function (btn,command) {
btn.onclick = function() {
command.execute()
}
}
{/* 编写点击按钮之后的具体行为 */}
var menuBar = {
refresh:function() {
console.log( '刷新菜单目录' );
}
}
var refreshMenuBarCommand= function(receiver) {
this.receiver = receiver
}
refreshMenuBarCommand.prototype.excute = function(){
this.receiver.refresh()
}
var refreshMenuBarCommand = new refreshMenuBarCommand( menuBar );
setCommand( btn, refreshMenuBarCommand );
//我们会感动奇怪,引入command和receiver这两个把简单的事情复杂化了
var bindClick = function(btn,fn){
btn.onClick = fn
}
var menuBar = {
refresh:function(){
console.log("xxxx")
}
}
bindClick(btn,menuBar.refresh)
//这种说法是正确的,在传统面向对象语言的命令模式实现中,命令模式将过程式的请求调用封装在command对象的execute中,命令的接收者被当成command对象的属性保存起来,同时约定执行命令的操作调用command.execute方法
代码实现(2.撤销命令,撤销是命令模式里一个非常有用的功能,试想一下开发围棋程序的时候,我们把每一步棋子的变化都封装成命令,则可以轻而易举地实现悔棋功能。同样还可以实现文本编辑器的crtl+z功能)
//现在页面有一个input文本框和一两个button按钮,文本框中可以输入一些数字,表示小球移动后的水平位置,小球在用户点击按钮后开始移动,然后还有一个取消按钮,小球回到移动之前的位置,当然我们可以在文本框中输入负值,点击移动按钮,我们将使用命令模式实现这两个功能
<div id="ball" style="position:absolute;background:#000;width:50px;height:50px"></div>
输入小球移动后的位置:<input id="pos"/>
<button id="moveBtn">开始移动</button>
<button id="cancelBtn">开始移动</button>
</body>
<script>
var ball = document.getElementById( 'ball' );
var pos = document.getElementById( 'pos' );
var moveBtn = document.getElementById( 'moveBtn' );
var cancelBtn = document.getElementById( 'cancelBtn' );
var setCommand = function(receiver,position) {
this.receiver = receiver;
this.position = position;
}
setCommand.prototype.execute = function(pos) {
this.oldValue = -pos.value;
this.receiver.start('left', pos.value, 1000, 'strongEaseOut' )
}
setCommand.prototype.undo = function() {
this.receiver.start('left', this.oldValue, 1000, 'strongEaseOut' )
}
var SetCommand;
moveBtn.onclick = function(){
var animate = new Animate( ball );
SetCommand = new setCommand(animate,pos);
SetCommand.execute()
};
cancelBtn.onclick = function(){
SetCommand.undo()
};
代码实现(3.重做,播放录像:我们把用户在键盘的输入都封装成命令,执行过的命令放到堆栈中,播放录像的时候只需要从头开始依次执行这些命令便可)
var Ryu = {
attack:function() {
console.log("防御")
},
defense:function(){
console.log("攻击")
},
jump:function() {
console.log("跳跃")
},
crouch:function(){
console.log("蹲下")
}
}
//模拟键盘
var A = { key:'attack'},W = { key:'defense'},D = { key:'jump'},S ={ key:'crouch'};
var setCommand = function(receiver,key) {
this.receiver = receiver;
this.key = key.key;
}
setCommand.prototype.excute = function() {
let _this = this;
return function () {
_this.receiver[_this.key]()
}
}
// 模拟按下a w s d
let a = [A,W,S,D];
let commandStack = [];
for(let i = 0; i < 4; i++){
var command = new setCommand(Ryu,a[i]);
let fn = command.excute();
fn();
commandStack.push(fn)
}
//模拟重新播放按钮
commandStack.forEach(fn=>fn())
</script>
js可以用高阶函数非常方便的实现命令模式,命令模式在js中是一种隐形的模式
网友评论