设计模式之发布命令模式

Posted by franki on November 4, 2018

command

命令模式

命令模式中的命令(command)指的是一个执行某些特定事情的指令

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

记住设计模式的主题,就是把变的事物和不变的事物分离开来。

实例: 假如有这样的一个需求,一个程序员需要负责写ui button,一个程序员负责js编写,那么这种状况下,负责ui的程序员并不知道button的接收者是谁,也不知道接收者接着用来做什么,这种情况下用命令模式更好的处理问题,其实负责ui的程序员只用完成写好接入的接口即可如下setCommand,至于以后做什么,在具体的命令对象里面完成

<div id="app">
    <button id='btn1'>按钮1</button>
    <button id='btn2'>按钮2</button>
    <button id='btn3'>按钮3</button>
 </div>

var btn1 = document.getElementById("btn1");
var btn2 = document.getElementById("btn2");
var btn3 = document.getElementById("btn3");

function setCommand(btn, command) {
    btn.onclick = function() {
        command.exculte();
    };
}

var refreshObj = {
    refresh: function() {
        console.log("refresh page");
    }
};

var subMenuObj = {
    add: function() {
        console.log("add menu");
    },
    delete: function() {
        console.log("delete menu");
    }
};

var refreshCommand = function(receiver) {
    this.receiver = receiver;
};

refreshCommand.prototype.exculte = function() {
    this.receiver.refresh();
};

var addMenuCommand = function(receiver) {
    this.receiver = receiver;
};

addMenuCommand.prototype.exculte = function() {
    this.receiver.add();
};

var deleteCommand = function(receiver) {
    this.receiver = receiver;
};

deleteCommand.prototype.exculte = function() {
    this.receiver.delete();
};

var refresher = new refreshCommand(refreshObj);
var adder = new addMenuCommand(subMenuObj);
var deleter = new deleteCommand(subMenuObj);

setCommand(btn1, refresher);
setCommand(btn2, adder);
setCommand(btn3, deleter);

从中可以看到我们是如何把请求发送者和请求接收者解耦开的

// 用闭包实现命令模式,执行更加简单,仅仅是执行回调函数即可

function setCommand(btn, command) {
    btn.onclick = function() {
        command.execute();
    };
}

var refreshObj = {
    refresh: function() {
        console.log("refresh page");
    }
};

var subMenuObj = {
    add: function() {
        console.log("add menu");
    },
    delete: function() {
        console.log("delte menu");
    }
};

function refreshCommand(obj) {
    return {
        execute: function() {
            obj.refresh();
        }
    };
}

function addCommand(obj) {
    return {
        execute: function() {
            obj.add();
        }
    };
}

function deleteCommand(obj) {
    return {
        execute: function() {
            obj.delete();
        }
    };
}

var refresher = refreshCommand(refreshObj);
var adder = addCommand(subMenuObj);
var deleter = deleteCommand(subMenuObj);
setCommand(btn1, refresher);
setCommand(btn2, adder);
setCommand(btn3, deleter);

无论是闭包实现命令模式还是用传统的面向对象实现的命令模式,其实都是把命令对象封装到一个地方,然后在保证在setCommand的函数当中可以被执行到即可。

命令模式之撤销

var btn = document.getElementById("btn");

var funObj = {
    attack: function() {
        console.log("attack");
    },
    defence: function() {
        console.log("defence");
    },
    jump: function() {
        console.log("jump");
    },
    crouch: function() {
        console.log("crouch");
    }
};

var commands = {
    "97": "defence", // a
    "100": "attack", // d
    "115": "crouch", // w
    "119": "jump" // s
};

function makeCommand(receiver, state) {
    if (!state) return;
        return function() {
        receiver[state]();
    };
}

var commandStack = [];
document.onkeypress = function(ev) {
    console.log(ev.keyCode);
    var key = ev.keyCode;

    var command = makeCommand(funObj, commands[key]);
    if (command) {
        command();
        commandStack.push(command);
    }
};

btn.onclick = function() {
    var comm;
    while ((comm = commandStack.shift())) {
        comm();
    }
};

命令模式在 JavaScript 语言中是一种隐形的模式