命令模式

191 阅读4分钟

简介

命令模式是最简单和优雅的模式之一,命令模式中的命令指的是一个执行某些特定的事情的命令。命令模式最常见的应用场景是:有时候需要向某些对象发送请求,但是并不知道请求的接收方是谁,也不知道被请求的操作是什么。此时希望用一种松耦合的方式来设计程序,使得请求发送者与请求接受者能够消除彼此之间的耦合关系。

下面带大家做一个案例:
   //假设设计一个计算器,包含四个按钮,加,减,乘,除
   /**
   * 第一步肯定是需要四个按钮
   * 第二步则是,给按钮绑定事件
   * */
//传统的书写方式
var plusbtn=document.getElementById("plus");//获取加按钮
var reducebtn=document.getElementById("reduce");//获取减按钮
var ridebtn=document.getElementById("ride");//获取乘按钮
var exceptbtn=document.getElementById("except");//获取除按钮

plusbtn.onclick=function(){
    console.log("加按钮处理的事情");
}
reducebtn.onclick=function(){
    console.log("减按钮处理的事情");
}
ridebtn.onclick=function(){
    console.log("乘按钮处理的事情");
}
exceptbtn.onclick=function(){
    console.log("除按钮处理的事情");
}

这种方式简单易懂,事件与dom耦合在一起,拓展,修改,只能去修改源码。下面看一下稍微好一点的代码。

简单的命令模式

var plusbtn=document.getElementById("plus");//获取加按钮
var reducebtn=document.getElementById("reduce");//获取减按钮
var ridebtn=document.getElementById("ride");//获取乘按钮
var exceptbtn=document.getElementById("except");//获取除按钮

/**
 * 绑定事件
 * @btn {dom} 按钮
 * @command {Object} 事件集合
 * */
function setComd(btn,command){
    btn.onclick=function(){
        command.execute();
    }
}
/**
 * 事件处理集合
 * */
var receiver={
    plusfn:function(){
       console.log("加按钮处理的事情");    
    },
    reducefn:function(){
       console.log("减按钮处理的事情");    
    },
    ridefn:function(){
       console.log("乘按钮处理的事情");    
    },
    excepfn:function(){
       console.log("除按钮处理的事情");    
    },
}
/**
 * 加函数
 * @receiver {Object} 事件集合
 * */
var plusfn=function(receiver){
    this.receiver=receiver;
}
plusfn.prototype.execute=function(){
    this.receiver.plusfn();
}
/**
 * 减函数
 * @receiver {Object} 事件集合
 * */
var reducefn=function(receiver){
    this.receiver=receiver;
}
reducefn.prototype.execute=function(){
    this.receiver.reducefn();
}

/**
 * 乘函数
 * @receiver {Object} 事件集合
 * */
var ridefn=function(receiver){
    this.receiver=receiver;
}
ridefn.prototype.execute=function(){
    this.receiver.ridefn();
}

/**
 * 除函数
 * @receiver {Object} 事件集合
 * */
var excepfn=function(receiver){
    this.receiver=receiver;
}
excepfn.prototype.execute=function(){
    this.receiver.excepfn();
}

var newplusfn=new plusfn(receiver);
var newreducefn=new reducefn(receiver);
var newridefn=new ridefn(receiver);
var newexcepfn=new excepfn(receiver);

setComd(plusbtn,newplusfn);
setComd(reducebtn,newreducefn);
setComd(ridebtn,newridefn);
setComd(exceptbtn,newexcepfn);

这种面向对象方式书写,也是很简单的命令模式,setComd函数只负责绑定事件,其他事情不管,receiver对象则是事件处理集合,把事件绑定与事件处理很好的解耦。

也许你就得书写这么多代码实现的功能跟上边的几行代码没有多大区别,你所说的是正确的。命令模式是将过程中的请求进行封装,通过封装调用execute方法,我们可以把运算块包装成型,receiver对象可以被四处传递,所以在调用命令的时候,客户不需要关心事情如何进行的。这就是命令模式。

撤销命令

命令有发送出去的,但有时候由于命令表达不清楚,就会选择撤销。

var plusbtn=document.getElementById("plus");//获取加按钮
var Revoke=document.getElementById("Revoke");//获取撤销按钮

/**
 * 绑定事件
 * @btn {dom} 按钮
 * @command {Object} 事件集合
 * */
function setComd(btn,command){
    btn.onclick=function(){
        command.execute();
    }
}
/**
 * 事件处理集合
 * */
var receiver={
    plusfn:function(old,n){    
       console.log("之前是:"+old+"加:"+n);
       return old*1+n*1;
    }
}
/**
 * 加函数
 * @receiver {Object} 事件集合
 * 
 * */
var plusfn=function(receiver){
    this.receiver=receiver;  // 
    this._value=null;//最新的值
    this.oldpos=null;//之前的值
}
plusfn.prototype.execute=function(){
    this.oldpos=document.getElementById("oreder").innerHTML;//存储之前的值
    this._value=this.receiver.plusfn(document.getElementById("first").value,document.getElementById("secend").value);//最新的值
    document.getElementById("oreder").innerHTML=this._value;
}
plusfn.prototype.undo=function(){
    this._value=this.oldpos;
    console.log("撤回:"+this._value);
    document.getElementById("oreder").innerHTML=this._value;
}

var newplusfn=new plusfn(receiver);

/**
 * 适配器
 * */
var Adapter={
    execute:function(){
        newplusfn.undo();
    }
}
setComd(plusbtn,newplusfn);

setComd(Revoke,Adapter);

这里涉及到知识的是适配器与撤回,撤回的意思就是在你计算之前,保存当前的一个新副本,点击撤销的时候,恢复到原状态。适配器顾名思义就是一个转接器,把两个相互不通的代码链接通。因为setComd函数接收的是一个对象,对象内包含execute方法,所以用Adapter做一个适配。

命令队列

就是把所有命令存放在数组之中,依次执行。现在来回一下,我们回到家里,首先是打开电视,打开音响,打开空调,关门,打开电脑,打开QQ,等一系列的命令,现在我们就来实现它。

/**
 * 命令队列
 * 
 * */

var MacroCommand=function(){
    return {
        commandList:[],
        add:function(command){
            this.commandList.push(command);
        },
        execute:function(){
            for(var i=0,command;command=this.commandList[i++];){
                command.execute();
            }
        }
    }
}

var opentv={
    execute:function(){
        console.log("打开电视");
    }
}
var opensound={
    execute:function(){
        console.log("打开音响");
    }
}
var open1=MacroCommand();
open1.add(opentv);
open1.add(opensound);
open1.execute();

宏命令

宏命令其实就是命令队列的集合,可以依次执行一批命令。想象一下家里好多设备需要打开,我们则需要一个按钮的万能遥控器触发他们打开。

/**
 * 强大的宏
 * 
 * */

var MacroCommand=function(){
    return {
        commandList:[],
        add:function(command){
            this.commandList.push(command);
        },
        execute:function(){
            for(var i=0,command;command=this.commandList[i++];){
                command.execute();
            }
        }
    }
}

var opentv={
    execute:function(){
        console.log("打开电视");
    }
}
var open1=MacroCommand();
open1.add(opentv);

/*----------------他们是一起的-------------------*/

var opensound={
    execute:function(){
        console.log("打开音响");
    }
}
var openkt={
    execute:function(){
        console.log("打开空调");
    }
}
var open2=MacroCommand();
open2.add(opensound);
open2.add(openkt);
/*----------------他们是一起的-------------------*/
var closedoor={
    execute:function(){
        console.log("关门");
    }
}

var opencomputer={
    execute:function(){
        console.log("打开电脑");
    }
}

var openqq={
    execute:function(){
        console.log("打开QQ");
    }
}
var open3=MacroCommand();
open3.add(closedoor);
open3.add(opencomputer);    
open3.add(openqq);

var openall=MacroCommand();            
openall.add(open1);            
openall.add(open2);    
openall.add(open3);    

openall.execute();

总结

命令模式是简单设计模式,希望大家能够理解。