命令模式(Command Pattern)是一種行為型設計模式,它將請求封裝為一個對象,使你可以用不同的請求對客戶進行參數化,還能支持請求的排隊、記錄日志及撤銷操作。這種模式將發送者和接收者解耦,發送者無需知道接收者的具體實現。
命令模式的核心角色
- Command(命令接口):聲明執行操作的接口(通常是
execute()
方法) - ConcreteCommand(具體命令):實現命令接口,綁定接收者和具體操作
- Receiver(接收者):執行命令所對應的操作,知道如何實施具體行為
- Invoker(調用者):要求命令執行請求,持有命令對象
- Client(客戶端):創建具體命令對象并設置其接收者
C++實現示例
以下以"智能家居控制系統"為例實現命令模式,支持對燈光、電視等設備的操作,以及命令的撤銷功能:
#include <iostream>
#include <string>
#include <vector>
#include <memory>// 前向聲明接收者類
class Light;
class Television;// 命令接口
class Command {
public:virtual ~Command() = default;virtual void execute() = 0;virtual void undo() = 0;virtual std::string getName() const = 0;
};// 接收者1:燈光
class Light {
private:std::string location;bool isOn;public:Light(std::string loc) : location(std::move(loc)), isOn(false) {}void on() {isOn = true;std::cout << location << "燈光 打開" << std::endl;}void off() {isOn = false;std::cout << location << "燈光 關閉" << std::endl;}bool getState() const {return isOn;}
};// 接收者2:電視
class Television {
private:std::string location;bool isOn;int channel;int volume;public:Television(std::string loc) : location(std::move(loc)), isOn(false), channel(1), volume(5) {}void on() {isOn = true;std::cout << location << "電視 打開" << std::endl;}void off() {isOn = false;std::cout << location << "電視 關閉" << std::endl;}void setChannel(int ch) {channel = ch;std::cout << location << "電視 頻道設置為: " << channel << std::endl;}void setVolume(int vol) {volume = vol;std::cout << location << "電視 音量設置為: " << volume << std::endl;}bool getState() const {return isOn;}int getChannel() const {return channel;}int getVolume() const {return volume;}
};// 具體命令1:開燈
class LightOnCommand : public Command {
private:Light* light;bool previousState; // 用于撤銷操作public:LightOnCommand(Light* l) : light(l) {}void execute() override {previousState = light->getState();light->on();}void undo() override {if (previousState) {light->on();} else {light->off();}std::cout << "撤銷 " << getName() << std::endl;}std::string getName() const override {return light->getState() ? "開燈" : "關燈";}
};// 具體命令2:關燈
class LightOffCommand : public Command {
private:Light* light;bool previousState;public:LightOffCommand(Light* l) : light(l) {}void execute() override {previousState = light->getState();light->off();}void undo() override {if (previousState) {light->on();} else {light->off();}std::cout << "撤銷 " << getName() << std::endl;}std::string getName() const override {return "關燈";}
};// 具體命令3:開電視
class TvOnCommand : public Command {
private:Television* tv;bool previousState;int previousChannel;int previousVolume;public:TvOnCommand(Television* t) : tv(t) {}void execute() override {previousState = tv->getState();previousChannel = tv->getChannel();previousVolume = tv->getVolume();tv->on();tv->setChannel(5); // 默認打開5頻道tv->setVolume(7); // 默認音量7}void undo() override {if (previousState) {tv->on();tv->setChannel(previousChannel);tv->setVolume(previousVolume);} else {tv->off();}std::cout << "撤銷 " << getName() << std::endl;}std::string getName() const override {return "開電視";}
};// 具體命令4:關電視
class TvOffCommand : public Command {
private:Television* tv;bool previousState;public:TvOffCommand(Television* t) : tv(t) {}void execute() override {previousState = tv->getState();tv->off();}void undo() override {if (previousState) {tv->on();} else {tv->off();}std::cout << "撤銷 " << getName() << std::endl;}std::string getName() const override {return "關電視";}
};// 調用者:遙控器
class RemoteControl {
private:std::vector<std::unique_ptr<Command>> onCommands;std::vector<std::unique_ptr<Command>> offCommands;std::unique_ptr<Command> undoCommand; // 記錄上一個命令用于撤銷public:RemoteControl(int slotCount) {// 初始化命令槽位onCommands.resize(slotCount);offCommands.resize(slotCount);}// 設置命令void setCommand(int slot, std::unique_ptr<Command> onCmd, std::unique_ptr<Command> offCmd) {if (slot >= 0 && slot < onCommands.size()) {onCommands[slot] = std::move(onCmd);offCommands[slot] = std::move(offCmd);}}// 按下開按鈕void pressOnButton(int slot) {if (slot >= 0 && slot < onCommands.size() && onCommands[slot]) {onCommands[slot]->execute();undoCommand = std::move(onCommands[slot]); // 保存命令用于撤銷}}// 按下關按鈕void pressOffButton(int slot) {if (slot >= 0 && slot < offCommands.size() && offCommands[slot]) {offCommands[slot]->execute();undoCommand = std::move(offCommands[slot]); // 保存命令用于撤銷}}// 按下撤銷按鈕void pressUndoButton() {if (undoCommand) {undoCommand->undo();} else {std::cout << "沒有可撤銷的操作" << std::endl;}}
};// 客戶端代碼
int main() {// 創建接收者Light* livingRoomLight = new Light("客廳");Light* bedroomLight = new Light("臥室");Television* livingRoomTv = new Television("客廳");// 創建命令auto livingRoomLightOn = std::make_unique<LightOnCommand>(livingRoomLight);auto livingRoomLightOff = std::make_unique<LightOffCommand>(livingRoomLight);auto bedroomLightOn = std::make_unique<LightOnCommand>(bedroomLight);auto bedroomLightOff = std::make_unique<LightOffCommand>(bedroomLight);auto tvOn = std::make_unique<TvOnCommand>(livingRoomTv);auto tvOff = std::make_unique<TvOffCommand>(livingRoomTv);// 創建遙控器(調用者),有3個槽位RemoteControl remote(3);// 給遙控器設置命令remote.setCommand(0, std::move(livingRoomLightOn), std::move(livingRoomLightOff));remote.setCommand(1, std::move(bedroomLightOn), std::move(bedroomLightOff));remote.setCommand(2, std::move(tvOn), std::move(tvOff));// 測試操作std::cout << "=== 執行一系列操作 ===" << std::endl;remote.pressOnButton(0); // 開客廳燈remote.pressOnButton(2); // 開客廳電視remote.pressOffButton(0); // 關客廳燈remote.pressOnButton(1); // 開臥室燈// 測試撤銷std::cout << "\n=== 測試撤銷操作 ===" << std::endl;remote.pressUndoButton(); // 撤銷"開臥室燈"remote.pressUndoButton(); // 撤銷"關客廳燈"remote.pressUndoButton(); // 撤銷"開客廳電視"// 清理資源delete livingRoomLight;delete bedroomLight;delete livingRoomTv;return 0;
}
代碼解析
-
命令接口(
Command
):定義了execute()
(執行命令)和undo()
(撤銷命令)方法,所有具體命令都需實現這兩個方法。 -
接收者(
Light
、Television
):實現具體的業務邏輯(如開燈、關燈、開電視等),命令對象會調用這些方法。 -
具體命令:
- 每個命令類綁定一個接收者和對應的操作(如
LightOnCommand
綁定Light
的on()
方法) - 實現
execute()
方法調用接收者的相應操作,并保存執行前的狀態用于undo()
- 每個命令類綁定一個接收者和對應的操作(如
-
調用者(
RemoteControl
):- 持有多個命令對象(開/關命令對),通過按鈕觸發命令執行
- 記錄上一個執行的命令,支持
undo()
操作
-
工作流程:客戶端創建命令并綁定接收者,調用者通過命令間接操作接收者,實現了發送者與接收者的解耦。
命令模式的優缺點
優點:
- 實現了發送者與接收者的解耦,發送者無需知道接收者的具體實現
- 容易擴展新命令,符合開放-封閉原則
- 支持命令的排隊、日志記錄和撤銷操作
- 可以組合多個命令形成復合命令(宏命令)
缺點:
- 可能導致系統中出現大量具體命令類,增加系統復雜度
- 命令的撤銷/重做功能實現復雜,需要保存歷史狀態
適用場景
- 當需要將請求發送者與接收者解耦時
- 當需要支持命令的撤銷、重做操作時
- 當需要將多個命令組合成復合命令時
- 當需要實現請求的排隊執行或日志記錄時
常見應用:
- 圖形界面中的菜單操作、按鈕點擊
- 事務處理(支持提交和回滾)
- 任務調度系統(命令排隊執行)
- 遙控器、游戲手柄等設備的操作抽象
命令模式通過將操作封裝為對象,為系統帶來了更大的靈活性和可擴展性,特別適合需要支持撤銷、日志、事務等功能的場景。