Javascript 设计模式 - 命令模式

785 阅读1分钟

命令模式将调用者和执行者之间分开,通过命令来映射各种操作,从而达到松耦合的目的。

命令模式的由来,其实是回调(callback)函数的一个面向对象的替代品。JavaScript 作为将函数作为一等对象的语言,跟策略模式一样,命令模式也早已融入到了 JavaScript 语言之中。

应用场景

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

命令模式的调用者只需要下达命令,不需要知道命令被执行的具体细节。从而使调用者专注于自己的主流程。

特点

  • 松耦合:将请求调用者和请求接收者松耦合
  • 生命周期
  • 支持撤销
  • 支持排队

举例

命令模式实现向左、向右、及撤销操作

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>命令模式</title>
    <style>
        #app{width: 100%;max-width: 400px;margin: 0 auto;}
        .container{width: 100%;background-color: #ddd;margin-top: 40px;}
        .box{width: 25px;height: 15px;background-color: #ff5b5c;}
    </style>
</head>
<body>
    <div id="app">
        <h1>命令模式</h1>
        <button id="left">向左</button>
        <button id="right">向右</button>
        <button id="turnback">撤销</button>
        <div style="margin-top: 20px"><strong> history: </strong><span id="history" ></span></div>
        <div class="container">
            <div id="box" class="box"></div>
        </div>
    </div>
<script>

const step = 25;
const right = document.getElementById('right');
const left = document.getElementById('left');
const turnback = document.getElementById('turnback');
const historyLog = document.getElementById('history');

class Box{
    constructor({pos = 0, id}) {
        this.pos = pos;
        this.$box = document.getElementById(id);
        this.update();
    }
    moveTo(pos) {
        this.pos = pos;
        this.update();
    }
    update() {
        this.$box.style.marginLeft = this.pos + 'px';
    }
}

class Command {
    constructor(box) {
        this.pos = box.pos || 0;
        this.box = box;
        this.history = [this.pos];
        historyLog.innerText = this.history;
    }
    run(dis) {
        this.pos = this.pos + dis;
        this.box.moveTo(this.pos);
        this.history.push(this.pos);
        historyLog.innerText = this.history;
        console.log('run:', this.pos, this.history);
    }
    cancel() {
        if (this.history.length === 0) {alert('回到起点');return}
        const record = this.history.pop();
        this.pos = this.history[this.history.length - 1];
        this.box.moveTo(this.pos);
        historyLog.innerText = this.history;
        console.log('cancel:', record, this.history);
    }
}

const box = new Box({id: 'box', pos: 2 * step});
const command = new Command(box);

right.addEventListener('click', () => command.run(step));
left.addEventListener('click', () => command.run(-step));
turnback.addEventListener('click', () => command.cancel());

</script>
</body>
</html>

参考