C++ 设计模式-命令模式

命令模式(Command Pattern)是一种行为设计模式,它将请求封装为一个对象,从而可以用不同的请求对客户进行参数化,并且支持请求的排队、记录日志以及撤销操作。命令模式的核心思想是将"请求"封装为一个对象,使得可以用不同的请求、队列或者日志来参数化其他对象。

示例:支持撤销操作的计算器

实现一个简单的计算器,它可以执行加法和减法操作,并且支持撤销上一次操作。


1. 定义命令接口

命令接口包含两个方法:execute(执行操作)和 unexecute(撤销操作)。

cpp 复制代码
class Command {
public:
    virtual ~Command() = default;
    virtual void execute() = 0;
    virtual void unexecute() = 0; // 撤销操作
};

2. 创建接收者类

接收者类是实际执行操作的对象。在这里,接收者是一个计算器,它可以执行加法和减法。

cpp 复制代码
class Calculator {
private:
    int value = 0; // 当前值

public:
    void add(int x) {
        value += x;
        std::cout << "Added " << x << ", current value: " << value << std::endl;
    }

    void subtract(int x) {
        value -= x;
        std::cout << "Subtracted " << x << ", current value: " << value << std::endl;
    }

    int getValue() const {
        return value;
    }
};

3. 创建具体命令类

我们为加法和减法分别创建具体的命令类。每个命令类都持有一个接收者对象(计算器)和一个操作数。

cpp 复制代码
// 加法命令
class AddCommand : public Command {
private:
    Calculator& calculator;
    int operand;

public:
    AddCommand(Calculator& calc, int x) : calculator(calc), operand(x) {}

    void execute() override {
        calculator.add(operand);
    }

    void unexecute() override {
        calculator.subtract(operand); // 撤销加法操作
    }
};

// 减法命令
class SubtractCommand : public Command {
private:
    Calculator& calculator;
    int operand;

public:
    SubtractCommand(Calculator& calc, int x) : calculator(calc), operand(x) {}

    void execute() override {
        calculator.subtract(operand);
    }

    void unexecute() override {
        calculator.add(operand); // 撤销减法操作
    }
};

4. 创建调用者类

调用者类(Invoker)负责执行命令,并且可以支持撤销操作。我们可以通过一个栈来记录执行过的命令,以便撤销。

cpp 复制代码
#include <stack>
#include <memory>

class Invoker {
private:
    std::stack<std::shared_ptr<Command>> commandHistory; // 命令历史记录

public:
    void executeCommand(std::shared_ptr<Command> command) {
        if (command) {
            command->execute();
            commandHistory.push(command); // 将命令记录到历史中
        }
    }

    void undo() {
        if (!commandHistory.empty()) {
            auto lastCommand = commandHistory.top();
            lastCommand->unexecute(); // 撤销上一次操作
            commandHistory.pop(); // 从历史记录中移除
        } else {
            std::cout << "No commands to undo." << std::endl;
        }
    }
};

5. 客户端代码

在客户端代码中,我们创建计算器对象、命令对象,并通过调用者执行和撤销操作。

cpp 复制代码
int main() {
    Calculator calculator;
    Invoker invoker;

    // 创建命令
    auto addFive = std::make_shared<AddCommand>(calculator, 5);
    auto subtractThree = std::make_shared<SubtractCommand>(calculator, 3);

    // 执行加法命令
    invoker.executeCommand(addFive); // 输出: Added 5, current value: 5

    // 执行减法命令
    invoker.executeCommand(subtractThree); // 输出: Subtracted 3, current value: 2

    // 撤销上一次操作
    invoker.undo(); // 输出: Added 3, current value: 5

    // 再次撤销
    invoker.undo(); // 输出: Subtracted 5, current value: 0

    // 尝试撤销空历史
    invoker.undo(); // 输出: No commands to undo.

    return 0;
}

输出结果

复制代码
Added 5, current value: 5
Subtracted 3, current value: 2
Added 3, current value: 5
Subtracted 5, current value: 0
No commands to undo.

总结

  1. 封装请求:将加法和减法操作封装为命令对象。
  2. 解耦调用者和接收者 :调用者(Invoker)不需要知道具体的操作细节,只需要调用命令对象的 executeunexecute 方法。
  3. 支持撤销操作:通过记录命令历史,可以轻松实现撤销功能。
相关推荐
minji...28 分钟前
Linux 线程同步与互斥(三) 生产者消费者模型,基于阻塞队列的生产者消费者模型的代码实现
linux·运维·服务器·开发语言·网络·c++·算法
CoderCodingNo3 小时前
【GESP】C++三级真题 luogu-B4499, [GESP202603 三级] 二进制回文串
数据结构·c++·算法
hetao17338374 小时前
2026-04-09~12 hetao1733837 的刷题记录
c++·算法
6Hzlia4 小时前
【Hot 100 刷题计划】 LeetCode 136. 只出现一次的数字 | C++ 哈希表&异或基础解法
c++·算法·leetcode
汉克老师5 小时前
GESP2024年6月认证C++三级( 第二部分判断题(1-10))
c++·数组·位运算·补码·gesp三级·gesp3级
无限进步_5 小时前
【C++】只出现一次的数字 II:位运算的三种解法深度解析
数据结构·c++·ide·windows·git·算法·leetcode
小贾要学习6 小时前
【Linux】TCP网络通信编程
linux·服务器·网络·c++·网络协议·tcp/ip
哎嗨人生公众号6 小时前
手写求导公式,让轨迹优化性能飞升,150ms变成9ms
开发语言·c++·算法·机器人·自动驾驶
code_whiter7 小时前
C++6(模板)
开发语言·c++