Command mode of JavaScript Design Mode [command decoupling]

Before explaining the command mode, let's first understand a command mode scenario in life:

Scenario 1:
Hospital treatment and prescription:
When you go to the hospital to see a doctor because of kidney deficiency, the doctor comes to the conclusion after some operation: you need to take a course of treatment [Xia Sangju] and [Xiaochaihu] (the drug name is purely fictional, and if you really have kidney deficiency, go to the doctor), so the doctor wrote out a drug list [Xia Sangju, Xiaochaihu] and asked you to take the drug list to the charging window; So he went to the charging window trembling and gave the medicine list [Xia Sangju, Xiaochaihu] to the charging staff. No matter who the doctor was, whether you had kidney deficiency or not, the charging staff just charged for the charging items of the medicine list, stamped a charging seal on the medicine list after completion, and then asked you to take the medicine list to the medicine taking window, so you ran to the medicine taking window and gave the medicine list [Xia Sangju, Xiaochaihu] to the staff, The staff went to each medicine cabinet to find the medicine you want and give it to you.

Scenario 2:
Kitchen production:
When you come to a restaurant for dinner, you tell the waiter that you want to eat [leek, okra and oyster], so the waiter will write it on the order paper [leek, okra and oyster], insert it into the back kitchen window, and the chefs will get the order paper [leek, okra and oyster], and then Honghong ha hey will make the dishes.
I believe it is easy for you to z find common ground from the above cases and their working mode.

For scenario 1:

  • Doctor: he doesn't know who charges you, how to charge, who gets you medicine and how to find it. He knew to give you a medicine list.
  • Toll collector: he doesn't know it's the medicine list for you. He just needs to pay you according to what's written on the medicine list, and no matter what you want to do next.
  • Medicine taker: he doesn't know who the doctor is and who pays the bill for you. As long as the medicine list is stamped, he will get you the medicine.
    -You: carry the medicine list and look for people everywhere. You don't care how the toll collector collects it for you or how the drug collector finds it. You just need to give the medicine list to them. They'll know what to do

For scenario 2:

  • You: you don't have to worry about who made the dishes for you and who gave you the order. You put forward your needs.
  • Waiter: generate an order according to your needs and give it to the back kitchen. I don't care how the back kitchen cooks.
  • Chef: I don't care who is eating and who is the waiter. I just need to get the order and make the dishes.

Conclusion: everyone only pays attention to the [list / order] and doesn't care what others do.

Quote the original words in JavaScript Design Patterns and development practice:
The most common application scenario of command mode: sometimes you need to send a request to some objects, but you don't know who the receiver of the request is or what the operation of the request is. At this time, you want to design the program in a loose coupling way, so that the coupling relationship between the request sender and the request receiver can be eliminated.

Use the example of scenario 2 to get the original words: sometimes [customer] needs to send a request [eat oysters...] to [chef], But [customer] actually doesn't know which chef made the request for [oyster] (even the chef in the next store may help with the oyster), nor does he know how the [chef] will make the dish after receiving the request for [oyster]. At this time, he hopes to have an intermediary [waiter] to help deal with the relationship between [customer] and [chef], so that [customer] and [chef] have no coupling relationship. The [waiter] is responsible for bringing the [customer] request order to the [chef].

Scenario 2 how to implement the case code:

First of all, we should clarify the roles involved in this model:
It consists of three roles:

  • Publisher invoker (issue command and hold command object)
  • receiver (command handler, do not know who initiated the request)
  • Command (receive the command, distribute it to the corresponding receiver for processing, and hold the receiver)

First, let's explain a very simple case:

  // case 

  // recipient
  class Receiver  {
    execute () {console.log('handle')};
  }
  // command 
  class Command {
    constructor (receiver) {
      this.receiver = receiver;
    }
    execute () {
      this.receiver.execute();
    }
  }
  // Publisher
  class Invoker {
    constructor (command) {
      this.command = command;
    }
    invoke () {
      this.command.execute();
    };
  }
  // implement
  let command = new Command(new Receiver());
  let invoker = new Invoker(command);
  invoker.invoke();

Combined with the scenario of case 2:

  // Chefs have all kinds of talents
  class Cook {
    makeVegetables () {
      console.log('make vegetable');
      return 'vegetable';
    }
    makeFish () {
      console.log('make fish');
      return 'fish';
    }
    execute () {
      this.makeFish();
      this.makeVegetables();
    }
  };

  // Command object
  class Command {
    constructor (cook) {
      this.cook = cook;
    } 
    execute () {this.cook.execute};
  }

  // Publisher of attendant command
  class Waiter {
    constructor (command) {
      this.command = command;
    }
    invokeCommand () {
      this.command.execute();
    };
  }

In fact, in js, as a first-class citizen, functions can be passed freely. There are ways to simplify the code:

  // Case --------------------------------- js simplification-----------------------------------------

  class Receiver  {
    execute () {console.log('Execution logic')};
  }
  class Invoker {
    invoke (receiver) {
      receiver.execute();
    };
  }
  let invoker = new Invoker();
  invoker.invoke(new Receiver());

I still recommend the above writing method. At least it looks clearer and the use of command mode is easier to understand.

Macro command

Is a set of commands. You can execute one set of commands at a time by executing macro commands.

Scene: if you buy a lot of Xiaomi smart appliances at home, you want to automatically turn on the lights, turn on the air conditioner and play music as soon as you come back from work; Isn't it beautiful.

  // Case --------------------------- macro command-----------------------------------------

  // Define command
  class OpenLightCommand {
    execute() {
      console.log('open light');
    }
  }
  class PlayMusicCommand {
    execute() {
      console.log('play light');
    }
  }
  class OpenAirConditioningCommand {
    execute() {
      console.log('open air conditioning');
    }
  }

  // Command set
  class MacroCommand {
    constructor() {
      this.commandList = [];
    }
    add(command) {
      this.commandList.push(command);
    }
    clear() {
      this.commandList = [];
    }
    execute() {
      for (let i = 0; i < this.commandList.length; i++) {
        this.commandList[i].execute();
      }
    }
  }

  class ITMan {
    constructor(command) {
      this.commamd = command;
    }
    whenGoHome() {
      this.commamd.execute();
    }
  }

  let macroCommand = new MacroCommand();
  macroCommand.add(new OpenLightCommand());
  macroCommand.add(new PlayMusicCommand());
  macroCommand.add(new OpenAirConditioningCommand());

  let itMan = new ITMan(macroCommand);
  itMan.whenGoHome();

Conclusion: the writing method of this model is very similar to the strategy model, but the purpose is completely different. The command mode is more to solve the decoupling between the requester and the implementer.

Tags: Javascript Design Pattern

Posted by AlGale on Sun, 22 May 2022 14:36:53 +0300