JavaScript 設計模式(七):命令模式

命令模式:請求以命令的形式包裹在對象中,並傳給調用對象。調用對象尋找可以處理該命令的合適的對象,並把該命令傳給相應的對象,該對象執行命令。

生活小栗子:客戶下單,訂單記錄了客戶購買的產品,倉庫根據訂單給客戶備貨。

模式特點

命令模式由三種角色構成:

  1. 發佈者 invoker(發出命令,調用命令對象,不知道如何執行與誰執行);
  2. 接收者 receiver (提供對應接口處理請求,不知道誰發起請求);
  3. 命令對象 command(接收命令,調用接收者對應接口處理髮布者的請求)。

發佈者 invoker 和接收者 receiver 各自獨立,將請求封裝成命令對象 command ,請求的具體執行由命令對象 command 調用接收者 receiver 對應接口執行。

命令對象 command 充當發佈者 invoker 與接收者 receiver 之間的連接橋樑(中間對象介入)。實現發佈者與接收之間的解耦,對比過程化請求調用,命令對象 command 擁有更長的生命週期,接收者 receiver 屬性方法被封裝在命令對象 command 屬性中,使得程序執行時可任意時刻調用接收者對象 receiver 。因此 command 可對請求進行進一步管控處理,如實現延時、預定、排隊、撤銷等功能。

代碼實現

class Receiver {  // 接收者類
  execute() {
    console.log('接收者執行請求');
  }
}

class Command {   // 命令對象類
  constructor(receiver) {
    this.receiver = receiver;
  }
  execute () {    // 調用接收者對應接口執行
    console.log('命令對象->接收者->對應接口執行');
    this.receiver.execute();
  }
}

class Invoker {   // 發佈者類
  constructor(command) {
    this.command = command;
  }
  invoke() {      // 發佈請求,調用命令對象
    console.log('發佈者發佈請求');
    this.command.cmd();
  }
}

const warehouse = new Receiver();       // 倉庫
const order = new Command(warehouse);   // 訂單
const client = new Invoker(order);      // 客戶
client.invoke();

/*
輸出:
  發佈者發佈請求
  命令對象->接收者->對應接口執行
  接收者執行請求
*/

應用場景

有時候需要向某些對象發送請求,但是並不知道請求的接收者是誰,也不知道被請求的操作是什麼。需要一種鬆耦合的方式來設計程序,使得發送者和接收者能夠消除彼此之間的耦合關係。
——《JavaScript 設計模式與開發實踐》

  1. 不關注執行者,不關注執行過程;
  2. 只要結果,支持撤銷請求、延後處理、日誌記錄等。

優缺點

  • 優點:
    • 發佈者與接收者實現解耦;
    • 可擴展命令,對請求可進行排隊或日誌記錄。(支持撤銷,隊列,宏命令等功能)。
  • 缺點:
    • 額外增加命令對象,非直接調用,存在一定開銷。

宏命令

宏命令:一組命令集合(命令模式與組合模式的產物)

發佈者發佈一個請求,命令對象會遍歷命令集合下的一系列子命令並執行,完成多任務。

// 宏命令對象
class MacroCommand {
  constructor() {
    this.commandList = [];  // 緩存子命令對象
  }
  add(command) {            // 向緩存中添加子命令
    this.commandList.push(command);
  }
  exceute() {               // 對外命令執行接口
    // 遍歷自命令對象並執行其 execute 方法
    for (const command of this.commandList) {
      command.execute();
    }
  }
}

const openWechat = {  // 命令對象
  execute: () => {
    console.log('打開微信');
  }
};

const openChrome = {  // 命令對象
  execute: () => {
    console.log('打開Chrome');
  }
};

const openEmail = {   // 命令對象
  execute: () => {
    console.log('打開Email');
  }
}

const macroCommand = new MacroCommand();

macroCommand.add(openWechat); // 宏命令中添加子命令
macroCommand.add(openChrome); // 宏命令中添加子命令
macroCommand.add(openEmail);  // 宏命令中添加子命令

macroCommand.execute();       // 執行宏命令
/* 輸出:
打開微信
打開Chrome
打開Email
*/

傻瓜命令與智能命令

傻瓜命令:命令對象需要接收者來執行客戶的請求。

智能命令:命令對象直接實現請求,不需要接收者,“聰明”的命令對象。

“傻瓜命令” 與 “智能命令” 的區別在於是否有 “接收者” 對象。

// openWechat 是智能命令對象,並沒有傳入 receiver 接收對象
const openWechat = {
  execute: () => {  // 命令對象直接處理請求
    console.log('打開微信');
  }
};

沒有 “接收者” 的智能命令與策略模式很類似。代碼實現類似,區別在於實現目標不同。

  1. 策略模式中實現的目標是一致的,只是實現算法不同(如目標:根據KPI計算獎金);
  2. 智能命令的解決問題更廣,目標更具散發性。(如目標:計算獎金/計算出勤率等)。

參考文章

本文首發Github,期待Star!
https://github.com/ZengLingYong/blog

作者:以樂之名
本文原創,有不當的地方歡迎指出。轉載請指明出處。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章