21.命令模式(Command Pattern)

定义

命令模式(Command Pattern) 是一种行为型设计模式,它将请求封装成一个对象,从而使您可以使用不同的请求、队列、日志请求以及支持撤销操作等功能。命令模式通过将请求(命令)封装成对象,使得客户端可以将请求发送者与请求接收者解耦,从而更灵活地控制操作的执行。

特性

  • 命令对象:将请求封装成一个命令对象,该命令对象包含了执行的具体操作。
  • Invoker(调用者):调用命令对象的 execute() 方法来执行相应的操作。
  • Receiver(接收者):实际执行命令的对象。
  • Client(客户端):客户端创建命令对象并设置命令的接收者。
  • Command(命令接口):定义命令接口,声明执行操作的抽象方法。

命令模式使得我们能够通过不同的命令对象来执行不同的操作,且操作的执行由调用者控制。

场景

适用场景

  • 请求调用者与请求接收者解耦:当客户端希望通过发送请求来调用不同的操作,而不希望知道具体如何执行时,可以使用命令模式。
  • 需要参数化对象:当需要参数化对象进行命令的请求时,命令模式可以封装请求的参数。
  • 支持撤销和恢复操作:命令模式非常适合实现撤销和恢复操作,通过存储命令对象及其执行过程,能够轻松地实现撤销功能。
  • 支持队列或日志请求:命令模式可以将请求封装成对象,方便将多个命令排入队列或记录日志。

应用场景

  • 图形用户界面(GUI)中的按钮点击事件:通过命令模式将按钮的点击事件封装为命令,使得不同按钮的操作可以被独立控制。
  • 事务管理系统:在事务管理中,命令可以表示一系列操作,通过命令模式进行回滚或恢复。
  • 多操作处理系统:在系统中可能有多个操作(如编辑操作),使用命令模式可以统一管理操作。

类设计

命令模式通常包括以下几个角色:

  1. Command(命令接口):声明执行操作的抽象方法。
  2. ConcreteCommand(具体命令类):实现了 Command 接口,封装了具体的请求与接收者之间的关系。
  3. Receiver(接收者):负责执行与请求相关的操作。
  4. Invoker(调用者):调用命令对象来执行请求。
  5. Client(客户端):客户端创建命令对象并设置接收者。

代码实现

我们设计一个 遥控器操作 的例子。遥控器上有多个按钮,每个按钮对应一个操作(如打开电视、关闭空调等)。我们使用命令模式来封装每个按钮的操作,并通过遥控器(调用者)来执行这些操作。

1. 定义命令接口(Command)

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

// 命令接口
class Command {
public:
    virtual void execute() = 0;  // 执行命令的接口
    virtual ~Command() {}
};
  • Command 是命令接口,声明了 execute() 方法,该方法将由具体命令类来实现。

2. 定义具体命令类(ConcreteCommand)

cpp 复制代码
// 电视打开命令
class TVOnCommand : public Command {
private:
    class TV* tv;  // 接收者(电视)

public:
    TVOnCommand(class TV* tv) : tv(tv) {}

    void execute() override {
        tv->turnOn();  // 执行命令:打开电视
    }
};

// 电视关闭命令
class TVOffCommand : public Command {
private:
    class TV* tv;

public:
    TVOffCommand(class TV* tv) : tv(tv) {}

    void execute() override {
        tv->turnOff();  // 执行命令:关闭电视
    }
};

// 空调开命令
class ACOnCommand : public Command {
private:
    class AC* ac;

public:
    ACOnCommand(class AC* ac) : ac(ac) {}

    void execute() override {
        ac->turnOn();  // 执行命令:打开空调
    }
};

// 空调关命令
class ACOffCommand : public Command {
private:
    class AC* ac;

public:
    ACOffCommand(class AC* ac) : ac(ac) {}

    void execute() override {
        ac->turnOff();  // 执行命令:关闭空调
    }
};
  • 每个命令类(如 TVOnCommand、TVOffCommand 等)都实现了 Command 接口,并封装了具体的操作逻辑。
  • 每个命令类持有一个接收者(例如 TV 或 AC),并在 execute() 方法中调用接收者的方法执行具体的操作。

3. 定义接收者类(Receiver)

cpp 复制代码
class TV {
public:
    void turnOn() {
        cout << "TV is turned ON." << endl;
    }

    void turnOff() {
        cout << "TV is turned OFF." << endl;
    }
};

class AC {
public:
    void turnOn() {
        cout << "AC is turned ON." << endl;
    }

    void turnOff() {
        cout << "AC is turned OFF." << endl;
    }
};
  • TV 和 AC 类是接收者,包含了执行具体操作的方法(例如打开电视、关闭空调)。

4. 定义调用者类(Invoker)

cpp 复制代码
class RemoteControl {
private:
    Command* command;  // 持有命令对象

public:
    void setCommand(Command* command) {
        this->command = command;  // 设置命令对象
    }

    void pressButton() {
        command->execute();  // 执行命令
    }
};
  • RemoteControl 类是调用者,持有一个命令对象并在按下按钮时执行该命令。

5. 客户端调用

cpp 复制代码
int main() {
    // 创建接收者对象
    TV* tv = new TV();
    AC* ac = new AC();

    // 创建命令对象
    Command* tvOn = new TVOnCommand(tv);
    Command* tvOff = new TVOffCommand(tv);
    Command* acOn = new ACOnCommand(ac);
    Command* acOff = new ACOffCommand(ac);

    // 创建遥控器
    RemoteControl* remote = new RemoteControl();

    // 按下按钮打开电视
    remote->setCommand(tvOn);
    remote->pressButton();

    // 按下按钮关闭电视
    remote->setCommand(tvOff);
    remote->pressButton();

    // 按下按钮打开空调
    remote->setCommand(acOn);
    remote->pressButton();

    // 按下按钮关闭空调
    remote->setCommand(acOff);
    remote->pressButton();

    // 清理内存
    delete tv;
    delete ac;
    delete tvOn;
    delete tvOff;
    delete acOn;
    delete acOff;
    delete remote;

    return 0;
}

6. 输出结果

cpp 复制代码
TV is turned ON.
TV is turned OFF.
AC is turned ON.
AC is turned OFF.
  • 客户端通过 RemoteControl 类来控制设备的开关,每次按下按钮时,遥控器都会调用相应命令对象的 execute() 方法,来完成实际的操作。

命令模式的优缺点

优点

  • 解耦发送者和接收者:命令模式将请求的发送者和接收者解耦,客户端不需要知道谁会处理请求,只需要发送命令对象即可。
  • 支持撤销和恢复:命令模式可以很容易实现撤销和恢复操作,命令对象可以保存执行过程,支持回滚。
  • 命令队列和日志:命令可以存储在队列中或日志中,方便管理和回溯。
  • 可扩展性:新命令的增加不会影响现有代码,只需要新增具体命令类即可。

缺点

  • 类的数量增加:每个命令都会对应一个具体的命令类,可能会增加类的数量。
  • 系统结构复杂:使用命令模式时,系统中需要管理多个命令类、调用者和接收者,可能使系统结构变得复杂。

场景

适用场景:

  • GUI事件处理:例如,按钮点击、菜单选择等GUI事件的处理,可以通过命令模式将每个事件封装为命令对象。
  • 任务调度系统:将任务封装成命令对象,通过队列或调度器执行任务。
  • 撤销/恢复功能:如文本编辑器、绘图软件等,需要提供撤销和重做的功能,命令模式能很方便地实现该功能。
  • 宏命令:多个命令可以组合成一个"宏命令",一起执行。

应用场景:

  1. 文本编辑器的撤销操作:用户进行文本编辑时,编辑操作可以封装为命令对象,撤销时,可以通过命令对象来恢复到之前的状态。
  2. 图形编辑器中的操作:在图形编辑器中,用户可以执行绘制、删除、移动等操作,每个操作都可以封装为命令对象,便于撤销和重做。
  3. 网络请求处理:网络请求可以封装为命令对象,每个请求可以通过命令对象来执行,便于管理请求的执行顺序和状态。

总结

命令模式通过将请求封装成命令对象,使得请求的发送者与接收者解耦。它可以帮助简化系统中的操作,支持撤销/恢复功能,并使得系统更具扩展性。命令模式适用于事件处理、任务调度、宏命令等场景,可以使系统的操作更加灵活和可管理。

相关推荐
shinelord明6 小时前
【再谈设计模式】命令模式~封装请求的指挥者
设计模式·命令模式
shanks665 天前
【PyQt】超级超级笨的pyqt计算器案例
pyqt·命令模式
w(゚Д゚)w吓洗宝宝了7 天前
命令模式 - 命令模式的设计思想
c++·命令模式
博一波12 天前
【设计模式-行为型】命令模式
设计模式·命令模式
计算机小混子15 天前
C++实现设计模式---命令模式 (Command)
c++·设计模式·命令模式
大懒猫软件15 天前
如何写出优秀的提示词?ChatGPT官方的六种方法
ai·chatgpt·prompt·命令模式
晚秋贰拾伍16 天前
设计模式的艺术-命令模式
运维·设计模式·运维开发·命令模式·开闭原则
Chris·Bosh16 天前
QT:控件属性及常用控件(3)-----输入类控件(正则表达式)
qt·正则表达式·命令模式
好好学习++19 天前
【HF设计模式】06-命令模式
java·c++·设计模式·命令模式