命令模式——C++实现

1. 命令模式简介

命令模式 是一种行为型模式
**命令模式的定义:**将请求封装成一个对象,使发出请求的责任和执行请求的责任分开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理

命令模式的意思就是将请求的调用者和请求的接受者(或者叫执行者)分开,将他们解耦。

打个比方,以饭店吃饭为例,我点了个地三鲜,"做一盘地三鲜"就是一个请求,"我"就是请求的调用者,"大厨"就是请求的执行者。

命令模式就是将请求封装成了一个类对象,这个类对象包含了请求的接受者(也就是执行者),以及接受者需要执行的操作,并且这个类对象对外只暴露出一个execute的接口,(很多时候也会增加一个redo接口,来实现命令的撤销)。

无论是谁拿到了这个类对象,都可以通过调用execute接口来完成这个请求,而无需知道请求是如何完成的,以及请求是由谁完成的。

命令模式通过封装请求,可以实现请求的传递、延迟调用、命令的撤销、重做等功能。

核心的概念已经讲清楚了,下面看代码演示。

2. C++代码演示

代码演示以一个万能遥控器为例子。

有一些家用电器,每个家用电器的打开方式不同,并且打开的接口名称也不同。

需要实现一个万能遥控器,这个万能遥控器有一定数量的插槽。每一个插槽可以控制一个家用电器,并且每个插槽都有开、关两个按钮。

万能遥控器还有一个undo、一个redo按钮,实现撤销上一个命令,以及重做上一个命令。

将家用电器的打开请求封装成命令对象,并且对外暴露出一个execute接口、一个undo接口、一个redo接口。就可以将命令对象插入到万能遥控器的插槽上,遥控器只知道命令对象上有execute接口即可,无需知道怎样驱动具体的电器如何工作。

无论将来新增加了何种的电器,只需要实现对应的命令对象,万能遥控器即可使用该电器。

如果不太理解,请仔细看代码的实现:

cpp 复制代码
#include <iostream>
#include <vector>
#include <memory>
#include <stack>
#include <deque>
using namespace std;

class Light
{
public:
    void turnOn()
    {
        cout << "Light turned on" << endl;
    }

    void turnOff()
    {
        cout << "Light turned off" << endl;
    }
};

class Fan
{
public:
    enum Speed { OFF,LOW, MEDIUM, HIGH };
    void turnOn(Speed speed)
    {
        this->speed = speed;
        cout << "Fan turned on at " << speed << " speed" << endl;
    }
    void turnOff()
    {
        cout << "Fan turned off" << endl;
    }
    Speed getSpeed()const {
        return speed;
    }
private:
    Speed speed;
};

class Command {
public:
    virtual void execute() = 0;
    virtual void undo() = 0;
};

class TurnOnLightCommand : public Command {
private:
    Light* light = nullptr;
public:
    TurnOnLightCommand(Light* light) {
        this->light = light;
    }
    void execute() {
        light->turnOn();
    }
    void undo() {
        light->turnOff();
    }
};

class TurnOffLightCommand : public Command {
private:
    Light* light = nullptr;
public:
    TurnOffLightCommand(Light* light) {
        this->light = light;
    }
    void execute() {
        light->turnOff();
    }
    void undo() {
        light->turnOn();
    }
};

class TurnOnFanCommand : public Command {
private:
    Fan* fan = nullptr;
    Fan::Speed speed = Fan::LOW;
public:
    TurnOnFanCommand(Fan* fan, Fan::Speed speed = Fan::LOW) {
        this->fan = fan;
    }
    void execute() {
        this->speed = speed;
        fan->turnOn(speed);
    }
    void undo() {
        fan->turnOff();
    }
};

class TurnOffFanCommand : public Command {
private:
    Fan* fan = nullptr;
    Fan::Speed speed = Fan::OFF;
public:
    TurnOffFanCommand(Fan* fan) {
        this->fan = fan;
    }
    void execute() {
        speed = fan->getSpeed();
        fan->turnOff();
    }
    void undo() {
        fan->turnOn(speed);
    }
};

class NoCommand : public Command {
public:
    void execute() {}
    void undo() {}
};

class RemoteControl
{
public:
    RemoteControl(unsigned int numCommands, unsigned int maxUndoSize = 50) {
        for (unsigned int i = 0; i < numCommands; i++) {
            OnCommands.push_back(shared_ptr<Command>(new NoCommand()));
            OffCommands.push_back(shared_ptr<Command>(new NoCommand()));
        }
        maxUndoSize = maxUndoSize;
    }
    void setCommand(unsigned int slot, Command* onCommand, Command* offCommand) {
        OnCommands[slot] = shared_ptr<Command>(onCommand);
        OffCommands[slot] = shared_ptr<Command>(offCommand);
    }
    void onButtonWasPushed(unsigned int slot) {
        OnCommands[slot]->execute();
        logUndo(OnCommands[slot]);
        clearRedoStack();
    }
    void offButtonWasPushed(unsigned int slot) {
        OffCommands[slot]->execute();
        logUndo(OffCommands[slot]);
        clearRedoStack();
    }
    void undoButtonWasPushed() {
        if (!undoStack.empty()) {
            shared_ptr<Command> command = undoStack.back();
            command->undo();
            logRedo(command);
            undoStack.pop_back();
        }
        else
            cout << "没有可撤销的命令" << endl;
    }
    void redoButtonWasPushed() {
        if (!redoStack.empty()) {
            shared_ptr<Command> command = redoStack.top();
            command->execute();
            logUndo(command);
            redoStack.pop();
        }
        else
            cout << "没有可重做的命令" << endl;
    }
private:
    void logUndo(shared_ptr<Command> command) {
        undoStack.push_back(command);
        if (undoStack.size() > maxUndoSize)
            undoStack.pop_front();
    }
    void logRedo(shared_ptr<Command> command) {
        redoStack.push(command);
    }
    void clearRedoStack() {
        if (!redoStack.empty())
            stack<shared_ptr<Command>>().swap(redoStack);
    }
private:
    vector<shared_ptr<Command>> OnCommands;
    vector<shared_ptr<Command>> OffCommands;
    deque<shared_ptr<Command>> undoStack;
    stack<shared_ptr<Command>> redoStack;
    unsigned int maxUndoSize = 50;
};

void main()
{
    Light* light = new Light();
    Fan* fan = new Fan();
    RemoteControl* remote = new RemoteControl(3); // 遥控器有3个插槽

    // 设置每个插槽的命令
    remote->setCommand(0, new TurnOnLightCommand(light), new TurnOffLightCommand(light));
    remote->setCommand(1, new TurnOnFanCommand(fan), new TurnOffFanCommand(fan));
    remote->setCommand(2, new TurnOnLightCommand(light), new TurnOffLightCommand(light));

    cout << "按下插槽0的打开按钮" << endl;
    remote->onButtonWasPushed(0);

    cout << "---------------------" << endl;
    cout << "按下插槽1的打开按钮" << endl;
    remote->onButtonWasPushed(1);

    cout << "---------------------" << endl;
    cout << "按下插槽1的关闭按钮" << endl;
    remote->offButtonWasPushed(1);

    cout << "---------------------" << endl;
    cout << "按下插槽2的打开按钮" << endl;
    remote->onButtonWasPushed(2);

    cout << "---------------------" << endl;
    cout << "按下撤销按钮" << endl;
    remote->undoButtonWasPushed();

    cout << "---------------------" << endl;
    cout << "按下重做按钮" << endl;
    remote->redoButtonWasPushed();

    cout << "---------------------" << endl;
    cout << "按下重做按钮" << endl;
    remote->redoButtonWasPushed();

    

    delete remote;
    delete light;
    delete fan;
}

下面是执行结果:

以上就是对命令模式的讲解,如果不太理解或者有讲错的地方欢迎在评论区批评指正!

相关推荐
晚风予卿云月43 分钟前
详解STL中stack_queue为什么选择deque作为默认容器
c++·stl·deque·stack_queue
charlie1145141911 小时前
精读C++20设计模式——结构型设计模式:代理模式
c++·学习·设计模式·代理模式·c++20·概论
序属秋秋秋4 小时前
《C++进阶之C++11》【可变参数模板 + emplace接口 + 新的类功能】
c++·笔记·学习·c++11·可变参数模板·emplace系列接口
Pocker_Spades_A4 小时前
C++程序设计上机作业(1)
开发语言·c++
Chen--Xing4 小时前
OpenMP并行化编程指南
c++·密码学·openmp
乱飞的秋天4 小时前
C++中的特殊成员函数
开发语言·c++
攻城狮7号5 小时前
【AI时代速通QT】第八节:Visual Studio与Qt-从项目迁移到多版本管理
c++·qt·跨平台·visual studio·qt vs tools
郝学胜-神的一滴5 小时前
QAxios研发笔记(一):在Qt环境下,构建Promise风格的Get请求接口
开发语言·c++·spring boot·qt·ajax·前端框架·软件工程
AA陈超5 小时前
虚幻引擎UE5专用服务器游戏开发-21 连招技能动画蒙太奇播放
c++·游戏·ue5·游戏引擎·虚幻
无敌最俊朗@11 小时前
C++ 序列容器深度解析:vector、deque 与 list
开发语言·数据结构·数据库·c++·qt·list