鏈接:C++ 設計模式
鏈接:C++ 設計模式 - 訪問器模式
命令模式(Command Pattern)是一種行為型設計模式,它將請求封裝成一個對象,從而使你可以用不同的請求對客戶進行參數化,對請求排隊或記錄請求日志,以及支持可撤銷的操作。
1. 問題分析
在開發中,我們經常需要向某個對象發送請求,但我們希望請求的發送者和接收者解耦。我們還可能需要對請求進行排隊、記錄日志,甚至支持撤銷操作。
命令模式通過將請求封裝成一個獨立的對象,使得請求的發送者和接收者解耦。每個命令對象都實現一個統一的接口,包含執行請求的方法。這樣,我們可以用不同的命令對象對客戶進行參數化,并且可以很容易地擴展新的命令。
2.實現步驟
- 定義命令接口(Command):聲明執行請求的方法。
- 實現具體命令類(ConcreteCommand):實現命令接口,執行具體的請求。
- 定義接收者類(Receiver):包含執行具體請求的方法。
- 定義調用者類(Invoker):持有命令對象,并在某個時刻調用命令對象的執行方法。
- 客戶端代碼(Client):創建具體命令對象,并將其傳遞給調用者。
3.代碼示例
以機器人示例。
3.1.定義命令接口
class Command {public:virtual ~Command() = default;virtual void execute() = 0;
};
3.2.實現具體命令類
// 接收者類:機器人
class Robot {public:void moveForward() { std::cout << "Robot moves forward" << std::endl; }void moveBackward() { std::cout << "Robot moves backward" << std::endl; }void turnLeft() { std::cout << "Robot turns left" << std::endl; }void turnRight() { std::cout << "Robot turns right" << std::endl; }
};
3.3.定義接收者類
// 具體命令類:前進
class MoveForwardCommand : public Command {public:MoveForwardCommand(Robot* robot) : robot_(robot) {}void execute() override { robot_->moveForward(); }private:Robot* robot_;
};
// 具體命令類:后退
class MoveBackwardCommand : public Command {public:MoveBackwardCommand(Robot* robot) : robot_(robot) {}void execute() override { robot_->moveBackward(); }private:Robot* robot_;
};
// 具體命令類:左轉
class TurnLeftCommand : public Command {public:TurnLeftCommand(Robot* robot) : robot_(robot) {}void execute() override { robot_->turnLeft(); }private:Robot* robot_;
};
// 具體命令類:右轉
class TurnRightCommand : public Command {public:TurnRightCommand(Robot* robot) : robot_(robot) {}void execute() override { robot_->turnRight(); }private:Robot* robot_;
};
3.4.定義調用者類
// 調用者類:遙控器
class RemoteControl {public:void setCommand(Command* command) { command_ = command; }void pressButton() {if (command_) {command_->execute();}}private:Command* command_ = nullptr;
};
3.5.客戶端代碼
int main() {// 創建接收者對象Robot robot;// 創建具體命令對象MoveForwardCommand moveForwardCommand(&robot);MoveBackwardCommand moveBackwardCommand(&robot);TurnLeftCommand turnLeftCommand(&robot);TurnRightCommand turnRightCommand(&robot);// 創建調用者對象RemoteControl remoteControl;// 設置命令并按下按鈕remoteControl.setCommand(&moveForwardCommand);remoteControl.pressButton();remoteControl.setCommand(&moveBackwardCommand);remoteControl.pressButton();remoteControl.setCommand(&turnLeftCommand);remoteControl.pressButton();remoteControl.setCommand(&turnRightCommand);remoteControl.pressButton();return 0;
}
4.C++函數對象
函數對象是一個重載了 operator() 的類,其實例可以像函數一樣被調用。函數對象的主要目的是將行為封裝到對象中,使得對象可以像函數一樣被調用。函數對象強調的是行為的封裝和靈活性。
4.1.定義函數對象類
// 函數對象類:前進
class MoveForward {public:MoveForward(Robot* robot) : robot_(robot) {}void operator()() { robot_->moveForward(); }private:Robot* robot_;
};
// 函數對象類:后退
class MoveBackward {public:MoveBackward(Robot* robot) : robot_(robot) {}void operator()() { robot_->moveBackward(); }private:Robot* robot_;
};
4.2.定義調用者類
// 調用者類:遙控器
class RemoteControl {public:void setCommand(std::function<void()> command) { command_ = command; }void pressButton() {if (command_) {command_();}}private:std::function<void()> command_;
};
4.3.客戶端代碼
int main() {// 創建接收者對象Robot robot;// 創建函數對象MoveForward moveForward(&robot);MoveBackward moveBackward(&robot);// 創建調用者對象RemoteControl remoteControl;// 設置命令并按下按鈕remoteControl.setCommand(moveForward);remoteControl.pressButton();remoteControl.setCommand(moveBackward);remoteControl.pressButton();return 0;
}
5.命令模式與函數對象的對比
5.1. 相似點
- 封裝行為:命令模式和函數對象都可以用于封裝行為,使得行為可以像對象一樣被傳遞和調用。
- 解耦:兩者都可以實現請求的發送者和接收者的解耦。
5.2. 不同點
-
設計意圖:
- 命令模式:主要用于將請求封裝成對象,從而支持請求的排隊、記錄日志、撤銷和重做等操作。
- 函數對象:主要用于將行為封裝到對象中,使得對象可以像函數一樣被調用,強調行為的靈活性和可組合性。
-
結構復雜度:
- 命令模式:通常包含多個角色(命令、具體命令、調用者、接收者),結構較為復雜。
- 函數對象:通常只需要一個包含 operator() 方法的類,結構較為簡單。
-
使用場景:
- 命令模式:適用于需要對請求進行排隊、記錄日志、支持撤銷和重做等操作的場景。
- 函數對象:適用于需要將行為封裝到對象中,并像函數一樣調用的場景。
命令模式和函數對象在C++中都可以用于封裝行為,但它們在設計意圖和使用場景上有所不同。命令模式主要用于將請求封裝成對象,從而支持請求的排隊、記錄日志、撤銷和重做等操作;而函數對象主要用于將行為封裝到對象中,使得對象可以像函數一樣被調用,強調行為的靈活性和可組合性。