命令模式
命令模式(Command Pattern)是一種行為設(shè)計模式,它將請求封裝為獨(dú)立對象,允許用戶參數(shù)化客戶端對象,并支持請求排隊(duì)、記錄請求日志、撤銷操作等高級功能。
命令模式包含以下主要角色:
Invoker(調(diào)用者):要求命令對象執(zhí)行請求,通常會持有命令對象,可以持有很多的命令對象。
Command(命令接口):聲明執(zhí)行操作的接口。
ConcreteCommand(具體命令):將一個接收者對象綁定于一個動作,調(diào)用接收者相應(yīng)的操作。
Receiver(接收者):知道如何實(shí)施與執(zhí)行一個請求相關(guān)的操作,任何類都可能作為一個接收者。
Client(客戶端):創(chuàng)建具體命令對象并設(shè)定其接收者。
嵌入式應(yīng)用案例
嵌入式中有些需求需要按組設(shè)置一些配置參數(shù),如果誤觸發(fā)了重置配置參數(shù)的操作,還需要能撤銷為上一次的設(shè)置。
例如,一個配置參數(shù)管理的場景:亮度、音量、溫度這三個參數(shù)的管理。要求:能夠撤銷到上一個配置狀態(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í)行時保存接收者狀態(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: %sn", cmd->description);
}
void?reset_undo(Command* cmd)?
{
? ? ResetConfigCommand* rcc = (ResetConfigCommand*)cmd;
? ? *rcc->config = rcc->previous_config; ?// 恢復(fù)之前配置
? ??printf("Reverted reset operationn");
}
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í)際存儲配置數(shù)據(jù)的對象不直接參與命令執(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: %sn", cmd->description);
? ? ? ? cmd->undo(cmd);
? ? }
}
5、客戶端(Client):
- 組裝命令對象配置命令與接收者的關(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?<stdio.h>
#include?<stdlib.h>
#include?<string.h>
// 配置參數(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°Cnn", 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: %sn", 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: %sn", cmd->description);
}
void?reset_undo(Command* cmd)?
{
? ? ResetConfigCommand* rcc = (ResetConfigCommand*)cmd;
? ? *rcc->config = rcc->previous_config; ?// 恢復(fù)之前配置
? ??printf("Reverted reset operationn");
}
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;
}
這個例子中具體命令只有一個:重置配置命令。使用命令模式可以很方便地擴(kuò)展其它命令,如:
創(chuàng)建一個批量設(shè)置命令:繼承命令接口,并實(shí)現(xiàn)對應(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: %sn", cmd->description);
}
void?batch_set_undo(Command* cmd)?{
? ? BatchSetCommand* bsc = (BatchSetCommand*)cmd;
? ? *bsc->config = bsc->previous_config; ?// 恢復(fù)之前配置
? ??printf("Reverted batch settingsn");
}
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?<iostream>
#include?<vector>
#include?<string>
#include?<memory>
// 配置參數(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 <<?"°Cnn";
? ? }
};
// 命令接口
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<std::unique_ptr<Command>> history;
? ??staticconstsize_t?MAX_HISTORY =?10;
public:
? ??void?executeCommand(std::unique_ptr<Command> 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 operationn";
? ? }
? ??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<ResetConfigCommand>(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):
解耦:分離請求發(fā)起者和執(zhí)行者
可擴(kuò)展:新增命令無需修改現(xiàn)有代碼
支持高級操作:內(nèi)置撤銷/重做功能
缺點(diǎn):
類膨脹:每個命令需單獨(dú)類
間接調(diào)用:增加系統(tǒng)復(fù)雜度
嵌入式場景適用性總結(jié)
在需要操作隊(duì)列、撤銷功能或硬件抽象層的嵌入式系統(tǒng)中可以考慮使用命令模式,簡單操作可直接調(diào)用避免過度設(shè)計。
碼字不易,如果文章對你有幫助,麻煩幫忙點(diǎn)贊、轉(zhuǎn)發(fā),謝謝大家!