命令模式
命令模式(Command Pattern)是一種行為設(shè)計(jì)模式,它將請(qǐng)求封裝為獨(dú)立對(duì)象,允許用戶參數(shù)化客戶端對(duì)象,并支持請(qǐng)求排隊(duì)、記錄請(qǐng)求日志、撤銷操作等高級(jí)功能。
命令模式包含以下主要角色:
-
Invoker(調(diào)用者):要求命令對(duì)象執(zhí)行請(qǐng)求,通常會(huì)持有命令對(duì)象,可以持有很多的命令對(duì)象。
-
Command(命令接口):聲明執(zhí)行操作的接口。
-
ConcreteCommand(具體命令):將一個(gè)接收者對(duì)象綁定于一個(gè)動(dòng)作,調(diào)用接收者相應(yīng)的操作。
-
Receiver(接收者):知道如何實(shí)施與執(zhí)行一個(gè)請(qǐng)求相關(guān)的操作,任何類都可能作為一個(gè)接收者。
-
Client(客戶端):創(chuàng)建具體命令對(duì)象并設(shè)定其接收者。
嵌入式應(yīng)用案例
嵌入式中有些需求需要按組設(shè)置一些配置參數(shù),如果誤觸發(fā)了重置配置參數(shù)的操作,還需要能撤銷為上一次的設(shè)置。
例如,一個(gè)配置參數(shù)管理的場(chǎng)景:亮度、音量、溫度這三個(gè)參數(shù)的管理。要求:能夠撤銷到上一個(gè)配置狀態(tài)。
結(jié)構(gòu)圖:
1、命令接口(Command):
- 定義所有命令的通用接口
- 包含執(zhí)行(
execute
)和撤銷(undo
)方法 - 包含操作描述(
description
)
typedef struct Command Command;
struct Command
{
void (*execute)(Command*);
void (*undo)(Command*);
char description[64];
};
2、具體命令(ResetConfigCommand):
- 持有接收者(
SystemConfig
)的引用 - 在執(zhí)行時(shí)保存接收者狀態(tài)(
previous_config
) - 實(shí)現(xiàn)具體的業(yè)務(wù)邏輯(重置配置)
typedef struct
{
Command base; // 繼承命令接口
SystemConfig* config;
SystemConfig previous_config;
} ResetConfigCommand;
void reset_execute(Command* cmd)
{
ResetConfigCommand* rcc = (ResetConfigCommand*)cmd;
rcc->previous_config = *rcc->config; // 保存當(dāng)前配置
// 重置為默認(rèn)值
rcc->config->brightness = 50;
rcc->config->volume = 50;
rcc->config->temperature = 22;
strcpy(cmd->description, "Reset all parameters");
printf("Executed: %s\n", cmd->description);
}
void reset_undo(Command* cmd)
{
ResetConfigCommand* rcc = (ResetConfigCommand*)cmd;
*rcc->config = rcc->previous_config; // 恢復(fù)之前配置
printf("Reverted reset operation\n");
}
Command* create_reset_command(SystemConfig* config)
{
ResetConfigCommand* cmd = malloc(sizeof(ResetConfigCommand));
cmd->base.execute = reset_execute;
cmd->base.undo = reset_undo;
cmd->config = config;
return (Command*)cmd;
}
3、 接收者(SystemConfig):
- 實(shí)際存儲(chǔ)配置數(shù)據(jù)的對(duì)象
- 不直接參與命令執(zhí)行流程
- 通過命令被間接操作
typedef struct
{
int brightness;
int volume;
int temperature;
} SystemConfig;
4、調(diào)用者(Invoker):
- 核心調(diào)度中心
- 管理命令歷史記錄
- 提供執(zhí)行和撤銷功能
- 不依賴具體命令類型
Command* history[MAX_HISTORY];
int history_count = 0;
void execute_command(Command* cmd)
{
cmd->execute(cmd);
if (history_count < MAX_HISTORY)
{
history[history_count++] = cmd;
}
}
void undo_last_command(void)
{
if (history_count > 0)
{
Command* cmd = history[--history_count];
printf("Undo: %s\n", cmd->description);
cmd->undo(cmd);
}
}
5、客戶端(Client):
- 組裝命令對(duì)象
- 配置命令與接收者的關(guān)系
- 觸發(fā)命令執(zhí)行流程
- 負(fù)責(zé)資源清理
void command_demo(void)
{
// 創(chuàng)建接收者
SystemConfig current_config = {60, 40, 30};
// 創(chuàng)建具體命令
Command* reset_cmd = create_reset_command(¤t_config);
// 通過調(diào)用者執(zhí)行命令
execute_command(reset_cmd);
// 通過調(diào)用者撤銷命令
undo_last_command();
// 清理資源
free(reset_cmd);
}
序列圖:
1、代碼實(shí)現(xiàn)
C語言:
#include
#include
#include
// 配置參數(shù)結(jié)構(gòu)
typedefstruct
{
int brightness; // 亮度 (0-100)
int volume; // 音量 (0-100)
int temperature; // 溫度 (10-30°C)
} SystemConfig;
// 打印配置
void print_config(const SystemConfig* config, const char* title)
{
printf("%s:\n", title);
printf(" Brightness: %d%%\n", config->brightness);
printf(" Volume: %d%%\n", config->volume);
printf(" Temperature: %d°C\n\n", config->temperature);
}
// 命令接口
typedefstruct Command Command;
struct Command
{
void (*execute)(Command*);
void (*undo)(Command*);
char description[64];
};
// 命令歷史記錄
#define MAX_HISTORY 10
Command* history[MAX_HISTORY];
int history_count = 0;
void execute_command(Command* cmd)
{
cmd->execute(cmd);
if (history_count < MAX_HISTORY)
{
history[history_count++] = cmd;
}
}
void undo_last_command(void)
{
if (history_count > 0)
{
Command* cmd = history[--history_count];
printf("Undo: %s\n", cmd->description);
cmd->undo(cmd);
}
}
// 重置配置命令
typedefstruct
{
Command base;
SystemConfig* config;
SystemConfig previous_config; // 保存重置前的完整配置
} ResetConfigCommand;
void reset_execute(Command* cmd)
{
ResetConfigCommand* rcc = (ResetConfigCommand*)cmd;
rcc->previous_config = *rcc->config; // 保存當(dāng)前配置
// 重置為默認(rèn)值
rcc->config->brightness = 50;
rcc->config->volume = 50;
rcc->config->temperature = 22;
strcpy(cmd->description, "Reset all parameters");
printf("Executed: %s\n", cmd->description);
}
void reset_undo(Command* cmd)
{
ResetConfigCommand* rcc = (ResetConfigCommand*)cmd;
*rcc->config = rcc->previous_config; // 恢復(fù)之前配置
printf("Reverted reset operation\n");
}
Command* create_reset_command(SystemConfig* config)
{
ResetConfigCommand* cmd = malloc(sizeof(ResetConfigCommand));
cmd->base.execute = reset_execute;
cmd->base.undo = reset_undo;
cmd->config = config;
return (Command*)cmd;
}
void command_demo(void)
{
printf("===== Command Pattern Demo =====\n");
// 批量設(shè)置系統(tǒng)配置
SystemConfig current_config = {60, 40, 30};
print_config(¤t_config, "Batch Config");
// 誤操作:重置配置
Command* reset_cmd = create_reset_command(¤t_config);
execute_command(reset_cmd);
print_config(¤t_config, "After Reset (Mistake)");
// 撤銷重置操作
printf("--- Undo reset command ---\n");
undo_last_command();
print_config(¤t_config, "After Undo Reset");
printf("================================\n");
// 清理內(nèi)存
free(reset_cmd);
}
int main(void)
{
command_demo();
return0;
}
這個(gè)例子中具體命令只有一個(gè):重置配置命令。使用命令模式可以很方便地?cái)U(kuò)展其它命令,如:
創(chuàng)建一個(gè)批量設(shè)置命令:繼承命令接口,并實(shí)現(xiàn)對(duì)應(yīng)批量設(shè)置命令的邏輯。
// 批量設(shè)置命令
typedefstruct {
Command base;
SystemConfig* config;
SystemConfig new_config;
SystemConfig previous_config;
} BatchSetCommand;
void batch_set_execute(Command* cmd) {
BatchSetCommand* bsc = (BatchSetCommand*)cmd;
bsc->previous_config = *bsc->config; // 保存當(dāng)前配置
*bsc->config = bsc->new_config; // 應(yīng)用新配置
snprintf(cmd->description, 50, "Batch set: B=%d%%, V=%d%%, T=%d°C",
bsc->new_config.brightness,
bsc->new_config.volume,
bsc->new_config.temperature);
printf("Executed: %s\n", cmd->description);
}
void batch_set_undo(Command* cmd) {
BatchSetCommand* bsc = (BatchSetCommand*)cmd;
*bsc->config = bsc->previous_config; // 恢復(fù)之前配置
printf("Reverted batch settings\n");
}
Command* create_batch_set_command(SystemConfig* config,
int brightness, int volume, int temp) {
BatchSetCommand* cmd = malloc(sizeof(BatchSetCommand));
cmd->base.execute = batch_set_execute;
cmd->base.undo = batch_set_undo;
cmd->config = config;
// 設(shè)置新配置值
cmd->new_config.brightness = brightness;
cmd->new_config.volume = volume;
cmd->new_config.temperature = temp;
return (Command*)cmd;
}
C++:
#include
#include
#include
#include
// 配置參數(shù)結(jié)構(gòu)
class SystemConfig {
public:
int brightness; // 亮度 (0-100)
int volume; // 音量 (0-100)
int temperature; // 溫度 (10-30°C)
SystemConfig(int b = 50, int v = 50, int t = 22)
: brightness(b), volume(v), temperature(t) {}
void print(const std::string& title) const {
std::cout << title << ":\n";
std::cout << " Brightness: " << brightness << "%\n";
std::cout << " Volume: " << volume << "%\n";
std::cout << " Temperature: " << temperature << "°C\n\n";
}
};
// 命令接口
class Command {
public:
virtual ~Command() = default;
virtual void execute() = 0;
virtual void undo() = 0;
virtual std::string getDescription() const = 0;
};
// 調(diào)用者 (Invoker)
class CommandInvoker {
private:
std::vector history;
staticconstsize_t MAX_HISTORY = 10;
public:
void executeCommand(std::unique_ptr cmd) {
cmd->execute();
if (history.size() < MAX_HISTORY) {
history.push_back(std::move(cmd));
}
}
void undoLastCommand() {
if (!history.empty()) {
std::cout << "Undo: " << history.back()->getDescription() << "\n";
history.back()->undo();
history.pop_back();
}
}
};
// 具體命令:重置配置命令
class ResetConfigCommand :public Command {
private:
SystemConfig& config;
SystemConfig previousConfig;
std::string description = "Reset all parameters";
public:
ResetConfigCommand(SystemConfig& cfg) : config(cfg) {}
void execute() override {
previousConfig = config;
config = SystemConfig(50, 50, 22);
std::cout << "Executed: " << description << "\n";
}
void undo() override {
config = previousConfig;
std::cout << "Reverted reset operation\n";
}
std::string getDescription() const override {
return description;
}
};
// 客戶端
void commandDemo() {
std::cout << "===== Command Pattern Demo =====\n";
// 批量設(shè)置系統(tǒng)配置
SystemConfig currentConfig(60, 40, 30);
currentConfig.print("Batch Config");
// 創(chuàng)建調(diào)用者
CommandInvoker invoker;
// 誤操作:重置配置
invoker.executeCommand(std::make_unique(currentConfig));
currentConfig.print("After Reset (Mistake)");
// 撤銷重置操作
std::cout << "--- Undo reset command ---\n";
invoker.undoLastCommand();
currentConfig.print("After Undo Reset");
std::cout << "======================================\n";
}
int main() {
commandDemo();
return0;
}
2、命令模式優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
- 解耦:分離請(qǐng)求發(fā)起者和執(zhí)行者
- 可擴(kuò)展:新增命令無需修改現(xiàn)有代碼
- 支持高級(jí)操作:內(nèi)置撤銷/重做功能
缺點(diǎn):
- 類膨脹:每個(gè)命令需單獨(dú)類
- 間接調(diào)用:增加系統(tǒng)復(fù)雜度
嵌入式場(chǎng)景適用性總結(jié)
在需要操作隊(duì)列、撤銷功能或硬件抽象層的嵌入式系統(tǒng)中可以考慮使用命令模式,簡(jiǎn)單操作可直接調(diào)用避免過度設(shè)計(jì)。
碼字不易,如果文章對(duì)你有幫助,麻煩幫忙點(diǎn)贊、轉(zhuǎn)發(fā),謝謝大家!