iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 19
0

一份雞排,要切不要辣。

 想像一個熟悉的情況,當我們晚上買宵夜時,先走到鹹酥雞攤,跟老闆說一份雞排,要切不要辣,此時你下了第一個命令。接著走到飲料攤,跟阿姨說一杯紅茶,半糖少冰,此時你下了第二個命令,最後我們開開心心地拿著雞排跟紅茶回家享用。從這個過程中,可以發現我們針對不同的攤位,給予不同命令,我們並不在乎雞排準備的過程,也不在乎是誰來炸雞排,但是我們在意的是拿到切好並且不辣的雞排,紅茶也是這樣的情況。通過這樣的模式,我們可以輕易地傳遞命令(command)客戶端(Client) 不需要理解背後的執行細節,甚至也不需要知道是透過 誰(receiver) 來執行,但是最終我們都能夠得到命令的結果。

命令模式

在命令模式中,我們想要解決的問題是命令發起者 (client) 及命令接收者 (receiver) 之間的耦合。我們利用一種鬆耦合的模式,當程式需要像某個物件發出請求,不需要知道接收者 (receiver) 是哪個物件,也不必擔心命令的實際執行過程,藉由透過一層抽象的介面來呼叫命令執行。由於執行細節已經被封裝在 command 這個物件中,因此當 command 物件被傳遞時,可以很輕易在任何時刻任何地方去執行真正的命令。下面我們可以實際來看看程式碼的範例。

命令模式的應用

以下是一個用 Javascript 命令模式的簡單實作,在此只敘述一些基本的情況,foodBooth 定義了真正實作的細節,createFoodCommand 創造出一層抽象的介面,並封裝執行的程序,foodCommand 是一個可易被傳遞的物件,並可藉由呼叫 execute 來得到結果。

從分工的角度來看,如果兩個人(A 及 B)同時開發這個 feature,A 負責實作邏輯,B 負責編排版面,兩人需要約定好的介面就是 foodCommand 中的 execute, A 只專注實作 execute 的邏輯,並不在乎在哪裡或何時被呼叫。B 只專注綁定 execute 到合適的目標及觸發的時機,並不在乎 execute 內部的實作邏輯。藉由命令模式,我們能夠降低 A 及 B 的耦合程度,進而增加程式的可讀性及維護性。

const foodBooth = {
  prepare() {
    console.log("炸雞排");
    return Promise.resolve(this);
  },
  cut() {
    console.log("剪雞排");
    return Promise.resolve(this);
  },
  putSeasoning() {
    console.log("調味");
    return Promise.resolve(this);
  },
  deliver() {
    console.log("銀貨兩訖");
    return Promise.resolve(this);
  }
};

const createFoodCommand = receiver => ({
  execute() {
    return receiver
      .prepare()
      .cut()
      .putSeasoning()
      .deliver();
  }
});

const foodCommand = createFoodCommand(foodBooth);

const setCommand = (target, eventType, command) => {
  target.addEventListener(eventType, command.execute);
};

const button = document.getElementById("button1");

setCommand(button, "click", foodCommand);

命令模式注意之處

以上是命令模式的基本應用,在進階的應用上,我們還可以將撤回執行結果的方式 (undo) 封裝在 command 當中,藉由執行 undo 來得到上次的結果。也可以實作命令佇列,更彈性的編排命令需求,來擴展更複雜的商業邏輯。

總結

命令模式有效地解耦呼叫者及執行者之間的關係,讓兩者能專注在自己的領域,藉由這個模式,我們可以提高程式碼的品質及專案維護性。

作者:Ron


上一篇
[Design Pattern] Iterator 迭代器模式
下一篇
[Design Pattern] Interpreter 解譯器模式
系列文
什麼?又是/不只是 Design Patterns!?32

尚未有邦友留言

立即登入留言