【C++】命令模式

发布于:2025-07-01 ⋅ 阅读:(18) ⋅ 点赞:(0)

命令模式(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++ 中实现请求封装和操作管理的重要工具,通过将请求转化为对象,使系统更加灵活、可扩展,并支持复杂的功能如撤销、队列和日志。


如果这篇文章对你有所帮助,渴望获得你的一个点赞!

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到