1、模式定义
命令模式,把请求与实现解耦,并封装成独立对象,从而使不同的请求对客户端的实现参数化。
命令模式把请求模块和实现模块解耦。命令模式把创建模块的逻辑封装在一个对象里,并让这个对象提供一个参数化的请求接口,通过调用这个接口实现对该对象内部方法的调用。
下面的代码结构,即是命令模式的结构框架:
var ViewCommand = (function() {
// 命令的集合
var Action = {
create: function() {},
display: function() {}
}
// 命令模式对外的接口
return function excute() {}
})();
2、命令模式 应用举例
现有一个图片应用,有两个操作,一是创建图片DOM结构,二是把图片显示在页面上。下面我们使用命令模式来实现这个应用。命令模式对象对外的接口,接受两个重要参数,一是命令名称 msg.command
,二是当前命令执行所需要的实参 msg.param
。
注:使用 HTML结构模板,可以减小DOM操作拼凑页面带来的性能开销。封装方法 formatString用于把 HTML结构模板转化成真实的HTML字符串。
var ViewCommand = (function() {
// 定义两个 HTML结构模板
var tpl = {
// 用于展示图片结构的 HTML模板
product: [
'<div>',
'<img src="{#src#}" />',
'<p>{#text#}</p>',
'</div>'
].join(''),
// 用于展示标题结构的 HTML模板
title: [
'<div class="title">',
'<div class="main">',
'<h2>{#title#}</h2>',
'<p>{#tips#}</p>',
'</div>',
'</div>'
].join('')
};
var html = '';
// 封装:把模板字符转化成真实 HTML字符串
function formatString(str, obj) {
return str.replace(/\{#(\w+)#\}/g, function(match, key) {
return obj[key];
});
}
// 方法集合
var Action = {
// 创建模块视图
create: function(data, view) {
if (data.length) {
for(var i=0; i<data.length; i++) {
html += formatString(tpl[view], data[i]);
}
} else {
html += formatString(tpl[view], data);
}
},
// 视图展示
display: function(container, data, view) {
if(data) {
this.create(data, view);
}
document.getElementById(container).innerHTML = html;
// 视图展示完成后,重置 html
html = '';
}
}
// 命令接口
return function excute(msg) {
// 转化成数组
msg.param = Object.prototype.toString.call(msg.param) === "[object Array]" ? msg.param : [msg.param];
// Action内部调用的方法引用了this,所以此处为了保证作用域this传入到Action中
Action[msg.command].apply(Action, msg.param);
}
})();
// 使用上述封装的 ViewCommand命令模式
var productData = [
{src: './1.png', text:'绽放的桃花'},
{src: './2.png', text:'阳光下的温馨'},
{src: './3.png', text:'镜头前的绿色'},
];
var titleData = {title: '夏日里的一片温馨', tips: '暖暖的温情带给人们家的感受'};
ViewCommand({command: 'display', param: ['title', titleData, 'title]});
ViewCommand({command: 'create', param: ['product', productData, 'product']});
图例
3、命令模式 应用再举例
Canvas应用有很多绘图API,每个API执行时所需要的参数也不尽相同。现在我们使用命令模式对Canvas进行封装,一方面隐藏了Canvas上下文(避免外界对其修改),另一方面让Canvas命令的调用更具一致。
var CanvasCommand = (function() {
var canvas = document.getElementById('canvas');
var 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;
},
strokeRect: 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 命令模式
CanvasCommand.excute([
{command: 'fillStyle', param: 'red'},
{command: 'fillRect', param: [20, 20, 100, 100]}
]);
4、小结
命令模式把要执行的命令进行封装,解决了命令发起者和命令执行者之间的耦合。每条命令,本质上就是一个操作。命令的使用者不必了解命令执行者(命令对象)的命令接口是如何实现的,也不必了解命令是如何接受和执行的。所有的命令都被存储在命令对象中。
在命令模式下,新的命令也很容易被加入到命令对象中去。命令的使用具有一致性,在一定程度上这简化了操作方法的使用。
END 2019-03-02
网友评论