【C++】命令模式

目錄

  • 一、模式核心概念與結構
  • 二、C++ 實現示例:遙控器與家電控制
  • 三、命令模式的關鍵特性
  • 四、應用場景
  • 五、命令模式與其他設計模式的關系
  • 六、C++ 標準庫中的命令模式應用
  • 七、優缺點分析
  • 八、實戰案例:數據庫事務命令
  • 九、實現注意事項
    • 如果這篇文章對你有所幫助,渴望獲得你的一個點贊!

命令模式(Command Pattern)是一種【行為型】設計模式,它將請求封裝為對象,從而使你可以用不同的請求對客戶端進行參數化,對請求排隊或記錄請求日志,以及支持可撤銷的操作。這種模式將發起請求的對象(客戶端)與執行請求的對象(接收者)解耦,通過命令對象作為中間層來協調兩者。

一、模式核心概念與結構

命令模式包含四個核心角色:

  1. 命令接口(Command):定義執行操作的接口,通常包含execute()方法。
  2. 具體命令(Concrete Command):實現命令接口,持有接收者的引用,并調用接收者的相應方法。
  3. 接收者(Receiver):知道如何執行與請求相關的操作,負責具體業務邏輯。
  4. 調用者(Invoker):持有命令對象并觸發執行,不直接與接收者交互。

二、C++ 實現示例:遙控器與家電控制

以下是一個經典的命令模式示例,演示如何用命令模式實現家電的遠程控制:

#include <iostream>
#include <string>
#include <memory>
#include <vector>// 接收者:家電
class Light {
public:void turnOn() { std::cout << "Light is on" << std::endl; }void turnOff() { std::cout << "Light is off" << std::endl; }
};class TV {
public:void turnOn() { std::cout << "TV is on" << std::endl; }void turnOff() { std::cout << "TV is off" << std::endl; }void setChannel(int channel) {std::cout << "TV channel set to " << channel << std::endl;}
};// 命令接口
class Command {
public:virtual ~Command() = default;virtual void execute() = 0;virtual void undo() = 0;  // 支持撤銷操作
};// 具體命令:開燈命令
class LightOnCommand : public Command {
private:std::shared_ptr<Light> light;public:LightOnCommand(std::shared_ptr<Light> l) : light(l) {}void execute() override {light->turnOn();}void undo() override {light->turnOff();}
};// 具體命令:關燈命令
class LightOffCommand : public Command {
private:std::shared_ptr<Light> light;public:LightOffCommand(std::shared_ptr<Light> l) : light(l) {}void execute() override {light->turnOff();}void undo() override {light->turnOn();}
};// 具體命令:電視命令
class TVOnCommand : public Command {
private:std::shared_ptr<TV> tv;public:TVOnCommand(std::shared_ptr<TV> t) : tv(t) {}void execute() override {tv->turnOn();tv->setChannel(1);  // 默認頻道}void undo() override {tv->turnOff();}
};// 調用者:遙控器
class RemoteControl {
private:std::shared_ptr<Command> onCommand;std::shared_ptr<Command> offCommand;std::vector<std::shared_ptr<Command>> history;  // 命令歷史,用于撤銷public:void setCommands(std::shared_ptr<Command> on, std::shared_ptr<Command> off) {onCommand = on;offCommand = off;}void pressOnButton() {if (onCommand) {onCommand->execute();history.push_back(onCommand);}}void pressOffButton() {if (offCommand) {offCommand->execute();history.push_back(offCommand);}}void pressUndoButton() {if (!history.empty()) {auto lastCommand = history.back();lastCommand->undo();history.pop_back();std::cout << "Undo last command" << std::endl;} else {std::cout << "Nothing to undo" << std::endl;}}
};// 客戶端代碼
int main() {// 創建接收者auto livingRoomLight = std::make_shared<Light>();auto tv = std::make_shared<TV>();// 創建具體命令auto lightOn = std::make_shared<LightOnCommand>(livingRoomLight);auto lightOff = std::make_shared<LightOffCommand>(livingRoomLight);auto tvOn = std::make_shared<TVOnCommand>(tv);// 創建調用者RemoteControl remote;// 設置命令remote.setCommands(lightOn, lightOff);// 使用遙控器std::cout << "=== Control Light ===" << std::endl;remote.pressOnButton();    // 開燈remote.pressOffButton();   // 關燈remote.pressUndoButton();  // 撤銷:重新開燈// 切換控制對象remote.setCommands(tvOn, lightOff);std::cout << "\n=== Control TV ===" << std::endl;remote.pressOnButton();    // 開電視remote.pressUndoButton();  // 撤銷:關電視return 0;
}

三、命令模式的關鍵特性

  1. 請求封裝
    • 將請求(操作)封裝為命令對象,使請求可以被存儲、傳遞和調用。
  2. 解耦調用者和接收者
    • 調用者(如遙控器)無需知道接收者(如家電)的具體實現,只需調用命令接口。
  3. 支持撤銷和重做
    • 通過在命令接口中定義undo()方法,可以實現操作的撤銷功能。
  4. 隊列和日志
    • 命令對象可以被序列化和存儲,支持請求的排隊、記錄和回放。

四、應用場景

  1. 撤銷 / 重做系統
    • 文本編輯器、圖形設計工具中的撤銷操作。
    • 數據庫事務的回滾機制。
  2. 異步任務執行
    • 線程池、消息隊列中的任務調度。
    • 網絡請求的異步處理。
  3. 菜單和按鈕系統
    • GUI 應用中的菜單項和按鈕操作。
    • 游戲中的動作命令(如攻擊、跳躍)。
  4. 宏錄制
    • 辦公軟件中的宏功能,記錄一系列操作并回放。
  5. 分布式系統
    • 遠程過程調用(RPC)中的命令傳遞。

五、命令模式與其他設計模式的關系

  1. 備忘錄模式
    • 命令模式實現操作的撤銷,備忘錄模式保存對象的狀態。
    • 兩者可結合使用,命令模式負責執行操作,備忘錄模式提供狀態恢復。
  2. 觀察者模式
    • 命令模式用于處理請求,觀察者模式用于事件通知。
    • 命令的執行可以觸發觀察者模式中的事件。
  3. 責任鏈模式
    • 命令模式將請求封裝為對象,責任鏈模式將請求傳遞給多個處理者。
    • 兩者可結合使用,責任鏈中的處理者可以是命令對象。

六、C++ 標準庫中的命令模式應用

  1. 函數對象(Functor)
    • std::function和函數指針可視為輕量級命令模式實現。
    • 例如,std::sort接受一個比較函數對象作為參數。
  2. 線程池
    • C++11 的std::threadstd::async可結合命令模式實現任務隊列。
  3. 信號與槽機制
    • Qt 框架中的信號與槽是命令模式的典型應用,信號觸發時執行槽函數。

七、優缺點分析

優點:

  • 解耦調用者和接收者:降低系統耦合度,提高可維護性。
  • 支持擴展性:易于添加新的命令類,符合開閉原則。
  • 支持撤銷和日志:方便實現復雜的操作管理功能。

缺點:

  • 類數量增加:每個具體命令都需要一個類,可能導致類膨脹。
  • 實現復雜度:對于簡單操作,使用命令模式可能顯得冗余。
  • 性能開銷:封裝請求為對象會引入額外的間接性,可能影響性能。

八、實戰案例:數據庫事務命令

以下是一個數據庫事務命令的實現示例,支持事務的提交和回滾:

#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <stack>// 接收者:數據庫連接
class Database {
public:void executeQuery(const std::string& query) {std::cout << "Executing query: " << query << std::endl;// 實際執行SQL查詢...}void commit() {std::cout << "Committing transaction" << std::endl;// 提交事務...}void rollback() {std::cout << "Rolling back transaction" << std::endl;// 回滾事務...}
};// 命令接口
class Command {
public:virtual ~Command() = default;virtual void execute() = 0;virtual void undo() = 0;
};// 具體命令:SQL查詢命令
class QueryCommand : public Command {
private:std::shared_ptr<Database> database;std::string query;bool isTransactional;public:QueryCommand(std::shared_ptr<Database> db, const std::string& q, bool transactional = false): database(db), query(q), isTransactional(transactional) {}void execute() override {database->executeQuery(query);}void undo() override {if (isTransactional) {database->rollback();} else {std::cout << "Non-transactional query cannot be undone" << std::endl;}}
};// 具體命令:事務提交命令
class CommitCommand : public Command {
private:std::shared_ptr<Database> database;public:CommitCommand(std::shared_ptr<Database> db) : database(db) {}void execute() override {database->commit();}void undo() override {std::cout << "Commit cannot be undone" << std::endl;}
};// 調用者:事務管理器
class TransactionManager {
private:std::shared_ptr<Database> database;std::stack<std::shared_ptr<Command>> commandStack;  // 命令棧,用于撤銷public:TransactionManager(std::shared_ptr<Database> db) : database(db) {}void executeCommand(std::shared_ptr<Command> command) {command->execute();commandStack.push(command);}void commit() {auto commitCmd = std::make_shared<CommitCommand>(database);commitCmd->execute();commandStack.push(commitCmd);}void rollbackLastCommand() {if (!commandStack.empty()) {auto lastCommand = commandStack.top();lastCommand->undo();commandStack.pop();std::cout << "Rolled back last command" << std::endl;} else {std::cout << "No commands to roll back" << std::endl;}}
};// 客戶端代碼
int main() {// 創建數據庫連接auto db = std::make_shared<Database>();// 創建事務管理器TransactionManager txManager(db);// 執行一系列命令std::cout << "=== Executing commands ===" << std::endl;txManager.executeCommand(std::make_shared<QueryCommand>(db, "BEGIN TRANSACTION", true));txManager.executeCommand(std::make_shared<QueryCommand>(db, "INSERT INTO users VALUES ('Alice')"));txManager.executeCommand(std::make_shared<QueryCommand>(db, "INSERT INTO users VALUES ('Bob')"));// 回滾最后一條命令std::cout << "\n=== Rolling back last command ===" << std::endl;txManager.rollbackLastCommand();// 提交事務std::cout << "\n=== Committing transaction ===" << std::endl;txManager.commit();// 嘗試回滾提交操作(不可行)std::cout << "\n=== Trying to roll back commit ===" << std::endl;txManager.rollbackLastCommand();return 0;
}

九、實現注意事項

  1. 撤銷機制設計
    • 確保命令的undo()方法能正確恢復系統狀態。
    • 對于某些不可撤銷的操作(如刪除文件),undo()可記錄日志或提示用戶。
  2. 命令參數化
    • 命令可以設計為接受參數,使其更加靈活(如SetChannelCommand可接受頻道號)。
  3. 宏命令(Composite Command)
    • 可以創建包含多個命令的宏命令,實現批量操作。
  4. 線程安全
    • 在多線程環境中,命令的執行可能需要同步機制(如互斥鎖)。

命令模式是 C++ 中實現請求封裝和操作管理的重要工具,通過將請求轉化為對象,使系統更加靈活、可擴展,并支持復雜的功能如撤銷、隊列和日志。


如果這篇文章對你有所幫助,渴望獲得你的一個點贊!

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/912475.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/912475.shtml
英文地址,請注明出處:http://en.pswp.cn/news/912475.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于librdkafka開發的C++客戶端,生產者生產發送數據失敗問題處理

我們的項目使用了開源的librdkafka庫&#xff0c;實現向kafka服務器生產發送數據的功能。使用的librdkafka的版本是1.9.0。 作為客戶端程序&#xff0c;在開發時和客戶協商確認后&#xff0c;支持了SASL_PLAINTEXT認證。以下概念解釋引用自通義千問AI SASL (Simple Authentic…

OpenGL之yaw、pitch、fov 和 lookAt

在 3D 圖形學中&#xff0c;yaw、pitch、fov 和 lookAt 都是控制攝像機&#xff08;Camera&#xff09;行為的關鍵參數&#xff0c;但它們的 作用層級 和 使用場景 不同。 1. yaw、pitch、fov 的作用 (1) yaw&#xff08;偏航角&#xff09; 作用&#xff1a;控制攝像機 左右…

STM32-第一節-新建工程,GPIO,點亮LED,蜂鳴器

一、新建工程&#xff1a; 1.Keil中新建工程&#xff0c;選擇開發板型號。 2.工程文件夾建立Start&#xff0c;Library等分類&#xff0c;復制模版工程中的文件到工程文件夾中。 3.在Keil中添加分組&#xff0c;添加文件。 4.工程選項設置&#xff1a; c/c中&#xff1a;Inc…

Rust標量、復合類型與自定義類型、第三方并發結構

以下是 Rust 中標量類型、對象類型&#xff08;含結構體、復合類型、堆分配類型&#xff09;以及常用第三方并發數據結構的完整分類、示例和區別對比&#xff0c;幫助你系統掌握它們的本質異同&#xff1a; &#x1f7e2; 一、標量類型&#xff08;Scalar Types&#xff0c;存儲…

基于STM32溫濕度檢測—串口顯示

基于STM32溫濕度檢測 &#xff08;仿真&#xff0b;程序&#xff09; 功能介紹 具體功能&#xff1a; 1.使用DHT11檢測溫濕度&#xff1b; 2.單片機處理完控制LCD1602顯示溫濕度&#xff1b; 3.單片機也通過串口顯示檢測到的溫濕度&#xff1b; 添加圖片注釋&#xff0c;不…

Windows 10 查詢 Nginx 進程教程

1. 打開命令提示符&#xff08;CMD&#xff09; 按 Win R&#xff0c;輸入 cmd&#xff0c;回車。或者在開始菜單搜索欄輸入“cmd”&#xff0c;選擇“命令提示符”。 2. 查看是否有正在運行的 Nginx 進程 輸入命令&#xff1a; tasklist | findstr nginx這個命令會列出所有…

使用 Kafka 優化物流系統的實踐與思考

使用 Kafka 優化物流系統的實踐與思考 在現代物流系統中&#xff0c;訂單處理、倉儲管理、運輸調度等環節復雜且實時性要求高。為了滿足異步解耦、高吞吐、高可用、事件驅動和數據可靠性等需求&#xff0c;Kafka 作為分布式消息隊列和流處理平臺&#xff0c;成為了我們的首選。…

Rust中模式匹配let Some(gas_price) = tx.gas_price用法

你問得非常好&#xff0c;let Some(gas_price) tx.gas_price 是 Rust 中的一種模式匹配寫法&#xff0c;它用于從 Option 類型中提取值。 ? 背景知識&#xff1a;什么是 Option&#xff1f; 在 Rust 中&#xff0c;如果一個值可能存在也可能不存在&#xff0c;就會用 Option…

什么是LLM大語言模型

什么是LLM大語言模型 LLM的全稱是&#xff0c;Large Language Model&#xff0c;簡稱LLM&#xff0c;翻譯為大語言模型&#xff0c;其核心是模擬人類語言的復雜規律&#xff0c;實現語義理解、推理分析、文本生成等任務&#xff0c;主要目的是實現能讀懂和說出人類語言的模型。…

雜談-架構時代演進

關于未來 5-10 年軟件系統演化方向 1. 云原生 ? 超云原生&#xff08;Post Cloud Native&#xff09; Kubernetes 平臺自治化&#xff1a; K8s Operator 日益強大&#xff0c;逐步具備自愈、自動擴縮容、自動調優能力。 云廠商與企業私有云逐步融合為一體…

如何查看服務器的運行日志?

&#x1f7e2; 一、Linux服務器 Linux日志都在**/var/log**目錄下&#xff0c;最常用的有&#xff1a; &#x1f4c2; 常用日志文件 文件內容/var/log/messages大部分系統日志&#xff08;CentOS常見&#xff09;/var/log/syslog系統消息日志&#xff08;Ubuntu/Debian常見&a…

在幸狐RV1106開發板上用gcc14.2本地編譯安裝postgresql 17.5數據庫

在幸狐RV1106開發板上用gcc14.2本地編譯安裝postgresql 17.5數據庫 編譯環境&#xff1a; RV1106G3 Linux luckfox-rv1106 5.10.160 #3 Fri Jun 27 14:16:20 AWST 2025 armv7l GNU/Linux BusyBox v1.36.1 gcc version 14.2.0 (GCC) GNU ld (GNU Binutils) 2.44 GNU Make 4.4 n…

Go語言中map[string]interface{} 和 map[string]string的區別

在 Go 語言中&#xff0c;map[string]interface{} 和 map[string]string 是兩種不同類型的 map&#xff0c;它們的主要區別在于值的類型以及這種差異帶來的使用場景和靈活性的不同。 1. 值的類型 map[string]interface{}&#xff1a;這里的 interface{} 表示 Go 中的空接口類型…

AdGuard Home 安裝及使用

AdGuard Home 是 AdGuard 開源的一個私人 DNS 服務端,只需在網關部署,即可實現全局域網的廣告攔截與隱私反追蹤。在 DNS 解析的過程中,匹配規則庫內的 URL 進行攔截,同時在客戶端中,還可以通過自定義過濾規則實現網頁 DOM 的攔截。 基于 Golang 編寫的 AdGuard Home,官方…

繞過 GraphQL 暴力破解保護

題目要求&#xff1a; 本實驗的用戶登錄機制由 GraphQL API 提供支持。API 終端節點有一個速率限制器&#xff0c;如果它在短時間內收到來自同一源的太多請求&#xff0c;則會返回錯誤。 要解決實驗問題&#xff0c;請暴力破解登錄機制以 .使用身份驗證實驗室密碼列表作為密碼…

C/C++ 使用rapidjson庫 操作Json格式文件(創建、插入、解析、修改、刪除)

目錄 一、前言 1.簡介 2.兼容性 3.下載 4.安裝 5.官方文檔 6.自述 二、封裝Json 1. 創建一個 Document 對象 2. "key":"value" 3. { } 4. [ ] 5. [ { }, { } ] 6. [ [ ], [ ] ] 7. { [ ], [ ] } 8. { { }, { } } 9. 將Document轉換為字符串…

免安裝一鍵修復網絡診斷 + 權限修復!打印機共享錯誤工具適配 Win7/10/11

各位打印小能手們&#xff01;你們有沒有遇到過共享打印機出問題&#xff0c;搞得自己焦頭爛額的情況&#xff1f;比如系統一更新&#xff0c;打印機就連不上&#xff0c;打印任務失敗&#xff0c;真的是讓人崩潰啊&#xff01;別慌&#xff0c;今天就給大家全面介紹一款打印機…

電腦分屏快捷鍵5

按window右箭頭&#xff1a; 按window左箭頭&#xff1a;

nt!CcFlushCache函數分析之nt!CcFindBcb

nt!CcFindBcb函數分析 第一部分&#xff1a; 1: kd> p nt!CcAcquireByteRangeForWrite0x377: 80a13c49 e866e4ffff call nt!CcFindBcb (80a120b4) 1: kd> t nt!CcFindBcb: 80a120b4 55 push ebp 1: kd> kc # 00 nt!CcFindBcb 01 nt!CcAcqu…

矩陣及矩陣快速冪

一.矩陣與模板 【模板】矩陣求和 時間限制&#xff1a;1秒 內存限制&#xff1a;128M 題目描述 給出兩個&#x1d45b;行&#x1d45a;列的矩陣&#xff0c;求兩個矩陣的和 輸入描述 第一行輸入兩個以空格分隔的整數&#x1d45b;,&#x1d45a;&#xff0c;表示矩…