命令模式
概念描述
命令模式(Command): 将请求与实现解耦并封装成独立的对象,从而使不同的请求对客户端的实现参数化
示例代码
命令模式我们可以看成是将创建模块的逻辑封装在一个对象里,这个对象提供一个参数化的请求接口,通过调用这个接口并传递一些参数实现调用命令对象内部中的一些方法。
对于命令模式来说,请求部分很简单,只需要按照给定的参数格式书写指令即可,所以实现部分封装才是重点,因为它要为请求部分提供所需方法。
下面我们来看一个具体的例子,我们通过命令模式来封装一个画布对象并实现调用命令对象在页面上画出一个长方形。
//实现对象
var CanvasCommand=(function(){
//获取canvas
var canvas=document.getElementById('canvas'),
//canvas元素的上下文引用对象缓存在命令对象的内部
ctx=canvas.getContext('2d');
//内部方法对象
var Action={
//填充色彩
fillStyle:function(c){
ctx.fillStyle=c;
},
//填充矩形
fillRect:function(x,y,width,height){
ctx.fillRect(x,y,width,height);
},
//描边色彩
strokeStyle:function(c){
ctx.strokeStyle=c;
},
//描边矩形
strikeRect:function(x,y,width,height){
ctx.strokeRect(x,y,width,height);
},
//填充字体
fillText:function(text,x,y){
ctx.fillText(text,x,y);
},
//开启路径
beginPath:function(){
ctx.beginPath();
},
//移动画笔触点
moveTo:function(x,y){
ctx.moveTo(x,y);
},
//画笔连线
lineTo:function(x,y){
ctx.lineTo(x,y);
},
//绘制弧线
arc:function(x,y,r,begin,end,dir){
ctx.arc(x,y,r,begin,end,dir);
},
//填充
fill:function(){
ctx.fill();
},
//描边
stroke:function(){
ctx.stroke();
}
}
return {
//命令接口
excute:function(msg){
//如果没有命令直接返回
if(!msg){
return;
}
//如果命令是一个数组,遍历执行多个命令
if(msg.length){
for(var i=0;i<msg.length;i++)
arguments.callee(msg[i]);
}else{
//执行一个命令
msg.param=Object.prototype.toString.call(msg.param)==="[object Array]"?msg.param:[msg.param];
Action[msg.command].apply(Action,msg.param);
}
}
}
})()
我们来调用一下在页面画一个红色的矩形
CanvasCommand.excute([
{command:'fillStyle',param:'red'},
{command:'fillRect',param:[20,20,250,150]}
])
我们看到现在有了这个对象,任何人想绘制图形都不需要依赖canvas了只要按照命令对象结构给出的命令格式,写一条命令即可
实例代码
那么在除了画布外在其他场景我们还能使用命令模式做什么呢?其实我们还可以使用命令模式实现自由的创建视图。
既然我们要做一个自由化创建的视图,那么我们肯定要动态展示不同的模块,所以创建元素这一需求就是变化的,因此创建元素方法、展示方法应该都被命令化
//命令实现模块
var viewCommand=(function(){
//方法集合
var Action={
//创建视图方法
create:function(){},
//展示视图方法
display:function(){}
}
return function excute(){}
})();
我们的命令对象框架已经搭建出来了,那么我们就要开始实现命令对象中的每一个方法了
由于创建视图的过程中如果单纯使用DOM操作拼凑页面的开销实在有些大,所以我们用格式化字符串模块来创建我们的视图页面
var viewCommand=(function(){
var Action={
create:function(data,view){
//解析数据如果是一个数组
if(data.length){
//遍历数组
for(var i=0;i<data.length;i++){
//将格式化之后的字符串缓存到html中
html+=formteString(tpl[view],data[i])
}
}else{
//直接格式化字符串缓存到html中
html+=formteString(tpl[view],data)
}
},
display:function(container,data,view){
if(data){
//根据指定数据创建视图
this.create(data,view);
}
document.getElementById('container').innerHTML=html;
//展示后清空缓存的字符串
html='';
}
}
var tpl={
product:[
//展示图片结构模板
'<div>',
'<img src="{#src#}"/>',
'<p>{#text#}</p>',
'</div>'
].join(''),
//展示标题结构模板
title:[
'<div class="title">',
'<div class="main">',
'<h2>{#title#}</h2>',
'<p>{#tips#}</p>',
'</div>',
'</div>'
].join('')
}
//格式化字符串缓存字符串
html='';
function formteString(str,obj){
return str.replace(/{#(w+)#}/g,function(match,key){
return obj[key]
})
}
命令接口
return function excute(msg){
//解析命令,如果msg.param不是数组则将其转化为数组
msg.param=Object.prototype.toString.call(msg.param)==="[object Array]"?
msg.param:[msg.param];
Action[msg.command].apply(Action,msg.param)
}
})();
好了现在我们的命令对象已经创建好了,我们来测试一下
//模拟图片展示数据,标题展示数据
var productData=[
{src:'img/HBuilder.png',text:'1'},
{src:'img/HBuilder.png',text:'2'},
{src:'img/HBuilder.png',text:'3'}],
titleData={title:'HBuilder',tips:'Hello HBuilder'};
//展示标题模块
viewCommand({
command:'display',
param:['title',titleData,'title']
})
//展示多张图片
viewCommand({
command:'display',
param:['product',productData,'product']
})
//创建图片
viewCommand({
command:'create',
param:[{src:'img/HBuilder.png',text:'Hello HBuilder'},'product']
})
//展示多张图片
viewCommand({
command:'display',
param:['product',productData,'product']
})
总结
命令模式是将执行的命令封装,解决命令的发起者与命令的执行者之间的耦合。每一条命令实质上是一个操作。
命令的使用者不必要了解命令的执行者(命令对象)的命令接口使如何实现的、命令是如何接受的。命令是如何执行的。所有的命令都被存储在命令对象中
命令模式的优点:
命令模式的优点是解决命令使用者之间的耦合。新的命令很容易加入到命令系统中供使用者使用。命令的使用具有一致性,多数的命令在一定程度上是简化操作方法的使用。
命令模式的缺点:
命令模式是对一些操作的封装,这就造成每执行一次操作就要调用命令对象增加了系统的复杂度
也谢谢大家看到这里:)如果你觉得我的分享还可以请点击推荐,分享给你的朋友让我们一起进步~
好了以上就是本次分享的全部内容,本次示例参考自JavaScript设计模式一书,让我们一点点积累一点点成长,希望对大家有所帮助。
欢迎转载,转载请注明作者,原文出处。