The command pattern separates the caller from the performer, mapping operations through commands to achieve loose coupling.

The origin of the command pattern is an object-oriented substitute for the callback function. JavaScript is a language that treats functions as first-class objects. Like the policy pattern, the command pattern has long been integrated into the JavaScript language.

Application scenarios

Sometimes you need to send a request to some object without knowing who the recipient of the request is or what operation is being requested. At this point, you want to design the program in a loosely coupled way so that the request sender and the request receiver can decouple from each other.

The caller in command mode only needs to issue the command and does not need to know the details of how the command is executed. This allows the caller to focus on his main process.

The characteristics of

  • Loose coupling: Loosely coupling the request caller and the request receiver
  • The life cycle
  • Support undo
  • Support line

For example,

Command mode implements left, right, and undo operations

<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Command mode</title>
    <style>
        #app{width: 100%;max-width: 400px;margin: 0auto; }.container{width: 100%;background-color: #ddd;margin-top: 40px; }.box{width: 25px;height: 15px;background-color: #ff5b5c; }</style>
</head>
<body>
    <div id="app">
        <h1>Command mode</h1>
        <button id="left">To the left</button>
        <button id="right">To the right</button>
        <button id="turnback">undo</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('Back to square one');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>
Copy the code

reference

  • JavaScript design patterns and development practices