
你用過 Ctrl+Z 嗎?那你就用過 Command Pattern 的成果了。
每一個操作被包成一個 command 物件,執行的時候記錄下來,undo 的時候反向操作。沒有 Command Pattern,你怎麼知道要「撤銷什麼」?
先講結論
Command Pattern 把「一次操作」封裝成一個物件。好處是這個物件可以被:儲存、排隊、記錄歷史、撤銷重做。呼叫端不需要知道操作的細節,只要說「執行」就好。
classDiagram class Invoker { -history : Command[] +executeCommand(command) void } class Command { <<abstract>> +execute()* void } class SubmitRequestCommand { -receiver : WorkflowReceiver -requestId : String +execute() void } class ApproveRequestCommand { -receiver : WorkflowReceiver -requestId : String +execute() void } class WorkflowReceiver { +submit(requestId) void +approve(requestId) void } Invoker o-- Command : 執行與記錄 Command <|-- SubmitRequestCommand Command <|-- ApproveRequestCommand SubmitRequestCommand --> WorkflowReceiver ApproveRequestCommand --> WorkflowReceiver
實戰:工作流程系統
一個請購單要經過「提交 → 審核 → 執行」。每一步都是一個 Command,Invoker 負責執行並記錄歷史。

class WorkflowReceiver {
submit(requestId) { console.log(`Request ${requestId} submitted`); }
approve(requestId) { console.log(`Request ${requestId} approved`); }
}
class SubmitRequestCommand {
constructor(receiver, requestId) {
this.receiver = receiver;
this.requestId = requestId;
}
execute() { this.receiver.submit(this.requestId); }
}
class ApproveRequestCommand {
constructor(receiver, requestId) {
this.receiver = receiver;
this.requestId = requestId;
}
execute() { this.receiver.approve(this.requestId); }
}
class Invoker {
constructor() { this.history = []; }
executeCommand(command) {
command.execute();
this.history.push(command); // 記錄下來,之後可以 undo
}
}
const receiver = new WorkflowReceiver();
const invoker = new Invoker();
invoker.executeCommand(new SubmitRequestCommand(receiver, 'REQ-001'));
invoker.executeCommand(new ApproveRequestCommand(receiver, 'REQ-001'));
console.log(`已執行 ${invoker.history.length} 個命令`);什麼場景最適合?
- Undo/Redo:文字編輯器、繪圖工具(每個操作是一個 Command,undo 就是反向執行)
- 排程系統:把 Command 放進 queue,按時間或順序執行
- 巨集:錄製一連串操作,之後一鍵重播
要付出什麼代價?
每個操作都要寫一個 Command class。如果你的操作種類很多,class 數量會爆炸。這時候可以考慮用 function 取代 class(JavaScript 的函式本身就是 first-class object,天然適合做 Command)。
Command Pattern 就像你的瀏覽器歷史記錄——每個「上一頁」都是一次 undo。只是有些歷史記錄你不想被別人看到。