C++软件设计模式之命令模式

C++设计模式中的命令模式是一种行为设计模式,它封装了一个请求作为一个对象,从而让你使用不同的请求把客户端与服务操作解耦。这种模式有着广泛的用途和多种变体。以下是对命令模式的详细解析:

动机与意图

  1. 解耦调用者和接收者:命令模式的主要动机是将请求的发送者与接收者解耦,使得调用者不需要知道接收者的具体实现细节。
  2. 支持可撤销操作:命令模式可以记录每个命令的执行状态,从而实现撤销和重做功能,这在一些需要支持撤销操作的场景中非常有用。
  3. 支持命令的排队和日志记录:命令对象可以被存储在一个队列中,从而实现命令的排队执行。同时,也可以记录每个命令的执行日志,方便后续的问题排查和分析。
  4. 增加系统的灵活性:命令模式允许在运行时动态地添加新的命令,而不需要修改已有的代码,使系统更加灵活,可以方便地扩展新的功能。

适用场合

  1. GUI操作:在图形用户界面程序中,常常需要处理用户点击事件,使用命令模式可以方便地处理这些事件,并支持撤销和重做功能。
  2. 多线程环境:在多线程环境中,命令模式可以用来管理线程任务的执行序列。
  3. 需要记录操作日志的场景:通过命令对象,可以记录每个操作的详细信息,方便后续审计和分析。

代码示例

1. GUI操作

假设我们有一个图形用户界面,用户可以通过点击按钮来执行某些操作,我们使用命令模式来处理这些按钮点击事件,并支持撤销和重做功能。

cpp 复制代码
#include <iostream>
#include <vector>

// 接收者类
class Receiver {
public:
    void action() {
        std::cout << "执行具体操作" << std::endl;
    }
};

// 命令接口
class Command {
public:
    virtual void execute() = 0;
    virtual void undo() = 0;
};

// 具体命令类
class ConcreteCommand : public Command {
private:
    Receiver* receiver;
public:
    ConcreteCommand(Receiver* receiver) : receiver(receiver) {}
    void execute() override {
        receiver->action();
    }
    void undo() override {
        std::cout << "撤销具体操作" << std::endl;
    }
};

// 调用者类
class Invoker {
private:
    Command* command;
    std::vector<Command*> commandHistory;
public:
    void setCommand(Command* cmd) {
        command = cmd;
    }
    void executeCommand() {
        command->execute();
        commandHistory.push_back(command);
    }
    void undoLastCommand() {
        if (!commandHistory.empty()) {
            commandHistory.back()->undo();
            commandHistory.pop_back();
        }
    }
};

// 按钮类
class Button {
private:
    Invoker* invoker;
    Command* command;
public:
    Button(Invoker* invoker, Command* command) : invoker(invoker), command(command) {}
    void click() {
        invoker->setCommand(command);
        invoker->executeCommand();
    }
};

int main() {
    Receiver* receiver = new Receiver();
    ConcreteCommand* command = new ConcreteCommand(receiver);
    Invoker* invoker = new Invoker();
    Button* button = new Button(invoker, command);

    // 用户点击按钮
    button->click();
    // 撤销上一次操作
    invoker->undoLastCommand();

    delete button;
    delete invoker;
    delete command;
    delete receiver;

    return 0;
}

2. 多线程环境

假设我们有一个多线程环境,需要管理任务的执行序列。我们可以使用命令模式来管理这些任务。

cpp 复制代码
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <condition_variable>

// 接收者类
class Receiver {
public:
    void action() {
        std::cout << "执行具体操作" << std::endl;
    }
};

// 命令接口
class Command {
public:
    virtual void execute() = 0;
};

// 具体命令类
class ConcreteCommand : public Command {
private:
    Receiver* receiver;
public:
    ConcreteCommand(Receiver* receiver) : receiver(receiver) {}
    void execute() override {
        receiver->action();
    }
};

// 任务队列类
class TaskQueue {
private:
    std::vector<Command*> queue;
    std::mutex mutex;
    std::condition_variable cv;
    bool done;
public:
    TaskQueue() : done(false) {}
    void addTask(Command* task) {
        std::lock_guard<std::mutex> lock(mutex);
        queue.push_back(task);
        cv.notify_one();
    }
    void processTasks() {
        while (true) {
            Command* task;
            {
                std::unique_lock<std::mutex> lock(mutex);
                cv.wait(lock, [this] { return !queue.empty() || done; });
                if (done && queue.empty()) {
                    return;
                }
                task = queue.front();
                queue.erase(queue.begin());
            }
            task->execute();
        }
    }
    void stop() {
        {
            std::lock_guard<std::mutex> lock(mutex);
            done = true;
        }
        cv.notify_one();
    }
};

int main() {
    Receiver* receiver = new Receiver();
    ConcreteCommand* command1 = new ConcreteCommand(receiver);
    ConcreteCommand* command2 = new ConcreteCommand(receiver);

    TaskQueue taskQueue;
    std::thread worker(&TaskQueue::processTasks, &taskQueue);

    taskQueue.addTask(command1);
    taskQueue.addTask(command2);

    taskQueue.stop();
    worker.join();

    delete command2;
    delete command1;
    delete receiver;

    return 0;
}

3. 需要记录操作日志的场景

假设我们需要记录每个操作的详细信息,可以使用命令模式来实现这一点。

cpp 复制代码
#include <iostream>
#include <vector>
#include <fstream>

// 接收者类
class Receiver {
public:
    void action() {
        std::cout << "执行具体操作" << std::endl;
    }
};

// 命令接口
class Command {
public:
    virtual void execute() = 0;
    virtual void log() = 0;
};

// 具体命令类
class ConcreteCommand : public Command {
private:
    Receiver* receiver;
    std::string logMessage;
public:
    ConcreteCommand(Receiver* receiver, std::string logMessage) : receiver(receiver), logMessage(logMessage) {}
    void execute() override {
        receiver->action();
    }
    void log() override {
        std::ofstream logFile("log.txt", std::ios_base::app);
        logFile << logMessage << std::endl;
        logFile.close();
    }
};

// 调用者类
class Invoker {
private:
    Command* command;
    std::vector<Command*> commandHistory;
public:
    void setCommand(Command* cmd) {
        command = cmd;
    }
    void executeCommand() {
        command->execute();
        command->log();
        commandHistory.push_back(command);
    }
};

int main() {
    Receiver* receiver = new Receiver();
    ConcreteCommand* command1 = new ConcreteCommand(receiver, "命令1执行");
    ConcreteCommand* command2 = new ConcreteCommand(receiver, "命令2执行");

    Invoker* invoker = new Invoker();
    invoker->setCommand(command1);
    invoker->executeCommand();
    invoker->setCommand(command2);
    invoker->executeCommand();

    delete invoker;
    delete command2;
    delete command1;
    delete receiver;

    return 0;
}

基于该模式特点的软件架构模式

基于命令模式的特点,可以构建一种灵活且可扩展的软件架构。在这种架构中,各个组件之间的耦合度被降低,使得系统更加容易维护和扩展。同时,由于命令对象可以被存储和传递,因此可以方便地实现操作的撤销、重做、排队和日志记录等功能。这种架构特别适用于需要灵活处理用户请求、支持撤销和重做操作、以及需要记录操作日志的系统。

总的来说,命令模式是一种非常实用的设计模式,它通过将请求封装为对象来解耦调用者和接收者,增加了系统的灵活性和可扩展性。同时,根据不同的需求和应用场景,可以灵活地运用命令模式的各种变体来满足实际需求。

相关推荐
半盏茶香2 分钟前
启航数据结构算法之雅舟,悠游C++智慧之旅——线性艺术:顺序表之细腻探索
c语言·开发语言·数据结构·c++·算法·机器学习·链表
tan180°16 分钟前
Cpp::哈希表的两种模拟实现方式(27)
数据结构·c++·哈希算法·散列表
qq_433554541 小时前
C++面向对象编程:纯虚函数、抽象类、虚析构、纯虚析构
开发语言·c++·算法
Java知识日历4 小时前
【内含例子代码】Spring框架的设计模式应用(第二集)
java·开发语言·后端·spring·设计模式
old_power5 小时前
Linux(Ubuntu24.04)安装Eigen3库
linux·c++·人工智能
捕鲸叉7 小时前
C++并发编程之内存屏障
c++·并发编程
ltwoxc9 小时前
04-c++类和对象(下)
c++
攻城丶狮12 小时前
【蓝桥杯比赛-C++组-经典题目汇总】
c++·算法·图论
求上进的小怪兽12 小时前
Px4 V2.4.8飞控Mavlink命令控制说明
c++
XLYcmy13 小时前
分布式练手:Client
c++·windows·分布式·网络安全·操作系统·c·实验源码