从C++编程入手设计模式——命令模式

从C++编程入手设计模式------命令模式

​ 命令模式是一个用指令封装请求的优雅方法。换而言之,对于一个复杂的系统,当我们发现,使用一系列的指令(Command)来操作对象的时候,这个设计模式就会显得非常的实用。我们经常遇到这样的场景:用户点击一个按钮,希望执行某个操作,比如保存文件、删除一条记录、或撤销上一步操作。我们通常会写一段代码来直接响应这个按钮事件。然而,如果我们希望这个操作是可以记录、撤销、重做,甚至延迟执行的,传统的方式就变得笨拙而混乱。

​ 这个时候,命令模式(Command Pattern)就派上用场了。它的核心思想很简单:把每一个操作封装成一个对象。这样,我们就可以把操作当作数据一样存储、传递、撤销甚至组合。命令模式中通常有几个角色:命令对象(Command)、接收者(Receiver)、调用者(Invoker)和客户端。命令对象负责封装"做什么";接收者是实际干活的人;调用者是触发命令的人;客户端负责把它们组合在一起。

​ 我们可以想象一个遥控器控制家电的例子。电灯是接收者,它有开和关的功能;开灯命令和关灯命令是命令对象;遥控器就是调用者,它保存着按钮和命令的映射;用户就是客户端,负责把命令装进遥控器并按下按钮。

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

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

// 接收者
class Receiver {
public:
    void action() {
        std::cout << "Receiver: Executing action.\n";
    }
};

// 具体命令
class ConcreteCommand : public Command {
private:
    std::shared_ptr<Receiver> receiver;
public:
    explicit ConcreteCommand(std::shared_ptr<Receiver> r) : receiver(std::move(r)) {}
    void execute() override {
        receiver->action();
    }
};

// 调用者
class Invoker {
private:
    std::vector<std::shared_ptr<Command>> commandQueue;
public:
    void addCommand(std::shared_ptr<Command> cmd) {
        commandQueue.push_back(std::move(cmd));
    }

    void run() {
        for (const auto& cmd : commandQueue) {
            cmd->execute();
        }
        commandQueue.clear();
    }
};

int main() {
    auto receiver = std::make_shared<Receiver>();
    auto command = std::make_shared<ConcreteCommand>(receiver);
    Invoker invoker;
    invoker.addCommand(command);
    invoker.run();
    return 0;
}

​ 如你所见,这个就是常见的命令模式的模板代码。

优缺点讨论

命令模式的一个好处是,它实现了调用者和接收者之间的彻底解耦。调用者并不知道命令怎么执行,它只知道有个命令对象可以被触发。这使得我们可以在不改变调用者的前提下,轻松替换命令,甚至在运行时动态改变行为。

此外,命令模式还为我们带来了其他附加功能。比如,可以将命令记录到列表中,支持撤销和重做;可以组合多个命令形成宏命令,实现批量操作;可以把命令序列化之后发到远程服务器执行,实现远程控制。

不过命令模式也有它的代价。每个操作都要定义一个命令类,当操作很多时,类的数量会迅速增加。此外,为了支持撤销,命令对象还需要记录足够的信息,这可能会带来额外的内存负担。

练习题:通过命令控制文本编辑器

描述

模拟一个文本编辑器,用户可以执行"添加文字"、"删除文字"等操作,每个操作为一个命令。

要求

  • 接收者 TextDocument,支持添加和删除字符
  • 命令:AddTextCommand, DeleteTextCommand
  • 支持撤销操作
  • Invoker 为 EditorInvoker,负责管理执行历史

modern-cpp-patterns-playground/Command/TextEditor at main · Charliechen114514/modern-cpp-patterns-playground

相关推荐
书院门前细致的苹果35 分钟前
设计模式大全:单例、工厂模式、策略模式、责任链模式
设计模式·责任链模式·策略模式
MZ_ZXD0011 小时前
springboot旅游信息管理系统-计算机毕业设计源码21675
java·c++·vue.js·spring boot·python·django·php
A星空1232 小时前
一、Linux嵌入式的I2C驱动开发
linux·c++·驱动开发·i2c
凡人叶枫3 小时前
C++中智能指针详解(Linux实战版)| 彻底解决内存泄漏,新手也能吃透
java·linux·c语言·开发语言·c++·嵌入式开发
会叫的恐龙3 小时前
C++ 核心知识点汇总(第六日)(字符串)
c++·算法·字符串
小糯米6013 小时前
C++顺序表和vector
开发语言·c++·算法
独望漫天星辰3 小时前
C++ 多态深度解析:从语法规则到底层实现(附实战验证代码)
开发语言·c++
王老师青少年编程4 小时前
2024年信奥赛C++提高组csp-s初赛真题及答案解析(阅读程序第3题)
c++·题解·真题·csp·信奥赛·csp-s·提高组
凡人叶枫5 小时前
C++中输入、输出和文件操作详解(Linux实战版)| 从基础到项目落地,避坑指南
linux·服务器·c语言·开发语言·c++
CSDN_RTKLIB5 小时前
使用三方库头文件未使用导出符号情景
c++