美文网首页
JavaScript设计模式 | 21-命令模式

JavaScript设计模式 | 21-命令模式

作者: 夏海峰 | 来源:发表于2019-03-02 16:07 被阅读12次

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

相关文章

网友评论

      本文标题:JavaScript设计模式 | 21-命令模式

      本文链接:https://www.haomeiwen.com/subject/wyzeuqtx.html