簡介
命令模式(Command Pattern)是一種資料驅動的設計模式,也是一種行為型設計模式。這種模式的請求以命令的形式包裹在物件中,並傳給呼叫物件。呼叫物件再尋找合適的物件,並把該命令傳給相應的處理者。即把請求或操作封裝成單個物件,並使其可以被引數化和延遲執行,這種方式將命令和執行者進行了有效解耦。
如果你需要透過操作來引數化物件,可使用命令模式。如果你想要將操作放入佇列中、操作的執行或者遠端執行操作, 可使用命令模式。如果你想要實現操作回滾功能,可使用命令模式。
作用
- 將不同命令按照抽象命令封裝成不同的物件,將這些命令放到呼叫者裡。
- 客戶透過呼叫者執行命令再去呼叫接受者的動作,順序為:客戶呼叫方->呼叫者->命令物件->接受者。
- 同其他物件一樣,命令也可以實現序列化,從而方便地寫入檔案或資料庫中,實現延遲執行。
實現步驟
- 建立一個抽象命令介面,實現基本的命令方法。
- 建立多個具體命令類,實現抽象命令介面,以來命令接收者。
- 建立命令接收者,也就是具體業務類,接受命令並執行動作。
- 建立命令呼叫者,這是一個聚合命令的類,新增命令和執行命令。
UML
Java程式碼
基礎命令介面
// Command.java 命令抽象介面 public interface Command { void execute(); }
具體命令類,可以多個命令
// BuyCommand.java 購買命令,操作receiver,實現了抽象命令類 public class BuyCommand implements Command { private StockReceiver stockReceiver; public BuyCommand(StockReceiver stockReceiver) { this.stockReceiver = stockReceiver; } // 命令類呼叫執行者的實際動作 public void execute() { System.out.println(this.getClass().getName() + "::execute() "); this.stockReceiver.buy(); } } // SellCommand.java 出售命令,操作receiver,實現了抽象命令類 public class SellCommand implements Command { private StockReceiver stockReceiver; public SellCommand(StockReceiver stockReceiver) { this.stockReceiver = stockReceiver; } // 命令類呼叫執行者的實際動作 public void execute() { System.out.println(this.getClass().getName() + "::execute() "); stockReceiver.sell(); } }
命令呼叫類
// CommandInvoker.java 命令呼叫類,透過關聯命令來執行命令的呼叫 public class CommandInvoker { private List<Command> commandList = new ArrayList<Command>(); // 儲存命令 public void takeOrder(Command command) { System.out.println(this.getClass().getName() + "::takeOrder() " + command.getClass().getName()); commandList.add(command); } // 統一執行 public void executeOrders() { System.out.println(this.getClass().getName() + "::executeOrders() "); for (Command command : commandList) { command.execute(); } commandList.clear(); } }
命令接收執行類
// StockReceiver.java 命令模式真正的執行類,不直接對外,透過command來呼叫 public class StockReceiver { private String name; private int num; public StockReceiver(String name, int num) { this.name = name; this.num = num; } public void buy() { System.out.println(this.getClass().getName() + "::buy() [name=" + this.name + " num=" + this.num + "]"); } public void sell() { System.out.println(this.getClass().getName() + "::sell() [name=" + this.name + " num=" + this.num + "]"); } public void setName(String name) { this.setName(name); } public String getName() { return this.name; } public void setNum(int num) { this.num = num; } public int getNum() { return this.num; } }
測試呼叫
/* * 命令模式是客戶端透過一個命令執行者invoker,去執行某個命令command。 * 而命令則呼叫了業務類receiver的具體動作,從而完成真正的執行。 * 這種方式將命令和執行者進行了有效解耦。 */ // 先宣告一個被操作物件,也就是接收者 StockReceiver stock1 = new StockReceiver("Apple", 200); // 再宣告具體的命令 BuyCommand buyCommand = new BuyCommand(stock1); SellCommand sellCommand = new SellCommand(stock1); // 最後宣告呼叫者,由呼叫者來執行具體命令 CommandInvoker invoker = new CommandInvoker(); invoker.takeOrder(buyCommand); invoker.takeOrder(sellCommand); invoker.executeOrders(); // 再執行一隻股票 StockReceiver stock2 = new StockReceiver("Google", 100); BuyCommand buyCommand2 = new BuyCommand(stock2); invoker.takeOrder(buyCommand2); invoker.executeOrders();
Go程式碼
基礎命令介面
// Command.go 命令抽象介面 type Command interface { GetName() string SetStockReceiver(stockReceiver *StockReceiver) Execute() }
具體命令類,可以多個命令
// BuyCommand.go 購買命令,操作receiver,實現了抽象命令類 type BuyCommand struct { Name string `default:"BuyCommand"` stockReceiver *StockReceiver } func (c *BuyCommand) GetName() string { return c.Name } func (c *BuyCommand) SetStockReceiver(stockReceiver *StockReceiver) { c.stockReceiver = stockReceiver } // 命令類呼叫執行者來自行真正的動作 func (c *BuyCommand) Execute() { fmt.Println("BuyCommand::Execute() ") c.stockReceiver.Buy() } // SellCommand.go 出售命令,操作receiver,實現了抽象命令類 type SellCommand struct { Name string `default:"BuyCommand"` stockReceiver *StockReceiver } func (s *SellCommand) GetName() string { return s.Name } func (s *SellCommand) SetStockReceiver(stockReceiver *StockReceiver) { s.stockReceiver = stockReceiver } // 命令類呼叫執行者來自行真正的動作 func (s *SellCommand) Execute() { fmt.Println("SellCommand::Execute() ") s.stockReceiver.Sell() }
命令呼叫類
// CommandInvoker.go 命令呼叫類,透過關聯命令來執行命令的呼叫 type CommandInvoker struct { Name string commandList []Command } func (c *CommandInvoker) GetName() string { return c.Name } // 儲存命令 func (c *CommandInvoker) TakeOrder(command Command) { fmt.Println("CommandInvoker::TakeOrder() " + command.GetName()) c.commandList = append(c.commandList, command) } // 統一執行 func (c *CommandInvoker) ExecuteOrders() { fmt.Println("CommandInvoker::ExecuteOrders() ") for _, command := range c.commandList { command.Execute() } // 命令執行後清除 c.commandList = c.commandList[:0] }
命令接收執行類
// StockReceiver.go 命令模式真正的執行類,不直接對外,透過command來呼叫 type StockReceiver struct { Name string Num int } func (s *StockReceiver) Buy() { fmt.Println("StockReceiver::Buy() [Name=" + s.Name + " Num=" + strconv.Itoa(s.Num) + "]") } func (s *StockReceiver) Sell() { fmt.Println("StockReceiver::Sell() [Name=" + s.Name + " Num=" + strconv.Itoa(s.Num) + "]") }
測試呼叫
// main包下的main入口方法 func main() { fmt.Println("test start:") /* * 命令模式是客戶端透過一個命令執行者invoker,去執行某個命令command * 而命令則呼叫了業務類receiver的具體動作,從而完成真正的執行 * 這種方式將命令和執行者進行了有效解耦。 */ // 先宣告一個被操作物件,也就是接收者 var stock1 = &src.StockReceiver{ Name: "Apple", Num: 200, } // 再宣告具體的命令 var buyCommand = &src.BuyCommand{ Name: "buyCommand", } buyCommand.SetStockReceiver(stock1) var sellCommand = &src.SellCommand{ Name: "sellCommand", } sellCommand.SetStockReceiver(stock1) // 最後宣告呼叫者,由呼叫者來執行具體命令 var invoker = &src.CommandInvoker{ Name: "invoker", } invoker.TakeOrder(buyCommand) invoker.TakeOrder(sellCommand) invoker.ExecuteOrders() // 再執行一隻股票 var stock2 = &src.StockReceiver{ Name: "Google", Num: 100, } var buyCommand2 = &src.BuyCommand{ Name: "buyCommand2", } buyCommand2.SetStockReceiver(stock2) invoker.TakeOrder(buyCommand2) invoker.ExecuteOrders() }
C語言程式碼
基礎物件定義
// func.h檔案,基礎命令結構體head #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <string.h> // 基礎命令結構體 typedef struct Command { char name[50]; struct StockReceiver *stock_receiver; void (*set_stock_receiver)(struct Command *command, struct StockReceiver *); void (*execute)(struct Command *); } Command; // 接受者物件 typedef struct StockReceiver { char name[50]; int num; void (*buy)(struct StockReceiver *); void (*sell)(struct StockReceiver *); } StockReceiver; StockReceiver *stock_receiver_constructor(char *name, int num); // 繼承命令結構體 typedef struct BuyCommand { char name[50]; struct StockReceiver *stock_receiver; void (*set_stock_receiver)(struct BuyCommand *command, struct StockReceiver *); void (*execute)(struct Command *); } BuyCommand; BuyCommand *buy_command_constructor(char *name); // 繼承命令結構體 typedef struct SellCommand { char name[50]; struct StockReceiver *stock_receiver; void (*set_stock_receiver)(struct SellCommand *command, struct StockReceiver *); void (*execute)(struct Command *); } SellCommand; SellCommand *sell_command_constructor(char *name); // 命令執行者 typedef struct CommandInvoker { char name[50]; void (*take_order)(struct CommandInvoker *invoker, Command *command); void (*execute_orders)(struct CommandInvoker *invoker); // 陣列命令列表,記錄待執行的命令物件 struct Command **command_list; // 陣列長度記錄 int command_list_size; // 若是柔性陣列,則放在結構體最後,可以動態追加內容 // struct Command *command_list[]; } CommandInvoker; CommandInvoker *command_invoker_constructor(char *name);
具體命令類,可以多個命令
// buy_command.c 購買命令,操作receiver,實現了抽象命令類 #include "func.h" // 購買命令,操作receiver,實現了抽象命令類 void set_buy_stock_receiver(BuyCommand *command, StockReceiver *receiver) { command->stock_receiver = receiver; } // 命令類呼叫執行者來自行真正的動作 void buy_command_execute(Command *command) { printf("\r\n BuyCommand::execute() [command->name=%s]", command->name); command->stock_receiver->buy(command->stock_receiver); } // 建立Buy命令物件 BuyCommand *buy_command_constructor(char *name) { Command *command = (Command *)malloc(sizeof(Command)); strncpy(command->name, name, 50); command->execute = &buy_command_execute; // 轉為BuyCommand BuyCommand *buy_command = (BuyCommand *)command; buy_command->set_stock_receiver = &set_buy_stock_receiver; return buy_command; } // sell_command.c 出售命令,操作receiver,實現了抽象命令類 #include "func.h" // 出售命令,操作receiver,實現了抽象命令類 void set_sell_stock_receiver(SellCommand *command, StockReceiver *receiver) { command->stock_receiver = receiver; } // 命令類呼叫執行者來自行真正的動作 void sell_command_execute(Command *command) { printf("\r\n SellCommand::execute() [command->name=%s]", command->name); command->stock_receiver->sell(command->stock_receiver); } // 建立Sell命令物件 SellCommand *sell_command_constructor(char *name) { Command *command = (Command *)malloc(sizeof(Command)); strncpy(command->name, name, 50); command->execute = &sell_command_execute; // 轉為SellCommand SellCommand *buy_command = (SellCommand *)command; buy_command->set_stock_receiver = &set_sell_stock_receiver; return buy_command; }
命令呼叫類
// command_invoker.c 命令呼叫類,透過關聯命令來執行命令的呼叫 #include "func.h" /* 命令呼叫類,透過關聯命令來實行命令的呼叫 在命令模式中,Invoker(呼叫者)是一個可選的元件, 它負責將Command物件傳遞給Receiver, 並呼叫Command物件的execute方法來執行命令。 Invoker在實現命令模式時可以有多種實現方式。 */ void print_command_list(Command **list, int command_list_size) { printf("\r\nThe current command_list:"); for (int i = 0; i < command_list_size; i++) { printf("\r\n [i=%d, command->name=%s]", i, list[i]->name); } } // 把命令儲存到呼叫者的命令列表 void invoker_take_order(CommandInvoker *invoker, Command *command) { printf("\r\n CommandInvoker::take_order() [invoker->name=%s, command->name=%s, invoker->command_list_size=%d]", invoker->name, command->name, invoker->command_list_size); // 列表長度增加1位 int new_command_list_size = invoker->command_list_size + 1; /* 如果採取柔性陣列,則無需申請新空間和複製內容 */ // 把原列表命令暫存下來 Command **old_command_list = invoker->command_list; // 給命令列表申請新空間 invoker->command_list = (Command **)calloc(new_command_list_size, sizeof(Command *)); // 複製原有命令到命令列表,如果採取柔性陣列則無需複製 for (int i = 0; i < invoker->command_list_size; i++) { invoker->command_list[i] = old_command_list[i]; } free(old_command_list); // 把新的命令新增列表最後 invoker->command_list[new_command_list_size - 1] = command; invoker->command_list_size = new_command_list_size; // 列印當前有多少命令 // print_command_list(invoker->command_list, invoker->command_list_size); } // 統一執行全部命令 void invoker_execute_orders(CommandInvoker *invoker) { printf("\r\n CommandInvoker::execute_orders() "); int command_list_size = invoker->command_list_size; Command **command_list = invoker->command_list; for (int i = 0; i < command_list_size; i++) { Command *command = command_list[i]; command->execute(command); command_list[i] = NULL; } // 命令執行完後清除命令列表 invoker->command_list_size = 0; invoker->command_list = (Command **)calloc(0, sizeof(Command *)); } // 初始化CommandInvoker命令物件 CommandInvoker *command_invoker_constructor(char *name) { printf("\r\n command_invoker_constructor() [name=%s]", name); CommandInvoker *invoker = (CommandInvoker *)malloc(sizeof(CommandInvoker)); strncpy(invoker->name, name, 50); invoker->command_list_size = 0; invoker->take_order = &invoker_take_order; invoker->execute_orders = &invoker_execute_orders; return invoker; }
命令接收執行類
// stock_receiver.c 命令模式真正的執行類,不直接對外,透過command來呼叫 #include "func.h" /* 命令模式真正的執行類,不直接對外,透過command來呼叫 */ void stock_receiver_buy(StockReceiver *stock_receiver) { printf("\r\n StockReceiver::buy() [name=%s num=%d]", stock_receiver->name, stock_receiver->num); } void stock_receiver_sell(StockReceiver *stock_receiver) { printf("\r\n StockReceiver::sell() [name=%s num=%d]", stock_receiver->name, stock_receiver->num); } // 建立StockReceiver命令物件 StockReceiver *stock_receiver_constructor(char *name, int num) { printf("\r\n stock_receiver_constructor() [name=%s, num=%d]", name, num); StockReceiver *receiver = (StockReceiver *)malloc(sizeof(StockReceiver)); strncpy(receiver->name, name, 50); receiver->num = num; receiver->buy = &stock_receiver_buy; receiver->sell = &stock_receiver_sell; return receiver; }
測試呼叫
#include "../src/func.h" int main(void) { printf("test start:\r\n"); /* * 命令模式是一種行為設計模式,它將請求或操作封裝成單個物件,並使其可以被引數化和延遲執行。 * 在命令模式中,客戶端透過一個命令執行者invoker,去執行某個命令command * 而命令則呼叫了業務類receiver的具體動作,從而完成真正的執行 * 這種方式將命令和執行者進行了有效解耦。 */ // 先宣告一個被操作物件,也就是接收者 StockReceiver *stocker_receiver1 = stock_receiver_constructor("Apple", 200); // 再宣告具體的命令 BuyCommand *buy_command = buy_command_constructor("buy_command"); buy_command->set_stock_receiver(buy_command, stocker_receiver1); SellCommand *sell_command = sell_command_constructor("sell_command"); sell_command->set_stock_receiver(sell_command, stocker_receiver1); // 最後宣告呼叫者,由呼叫者來執行具體命令 CommandInvoker *invoker = command_invoker_constructor("invoker"); invoker->take_order(invoker, (Command *)buy_command); invoker->take_order(invoker, (Command *)sell_command); invoker->execute_orders(invoker); // 再執行一隻股票,宣告新的接受者 StockReceiver *stock_receiver2 = stock_receiver_constructor("Google", 100); BuyCommand *buy_command2 = buy_command_constructor("buy_command2"); // 這次只有buy命令 buy_command2->set_stock_receiver(buy_command2, stock_receiver2); // 還用原來的invoker,或者新建invoker invoker->take_order(invoker, (Command *)buy_command2); invoker->execute_orders(invoker); return 0; }
更多語言版本
不同語言實現設計模式原始碼:https://github.com/microwind/design-pattern