在 C++/Qt 中,命令模式(Command Pattern)的实现通常用于封装操作请求、支持撤销/重做(Undo/Redo)或解耦调用者与接收者。以下是几种常见的实现方式及示例:
1. Qt 的 QUndoCommand
和 QUndoStack
(内置的撤销/重做框架)
这是 Qt 中命令模式的最典型实现,用于管理可撤销的操作。
核心类:
QUndoCommand
:基类,表示一个可撤销的命令,需实现undo()
和redo()
。QUndoStack
:管理命令历史记录的栈,支持撤销/重做。
示例代码:
class AddItemCommand : public QUndoCommand { public: AddItemCommand(QListWidget *listWidget, const QString &text, QUndoCommand *parent = nullptr) : QUndoCommand("Add Item", parent), m_listWidget(listWidget), m_text(text) {} void redo() override { m_listWidget->addItem(m_text); } void undo() override { QListWidgetItem *item = m_listWidget->takeItem(m_listWidget->count() - 1); delete item; } private: QListWidget *m_listWidget; QString m_text; }; // 使用示例 QUndoStack undoStack; QListWidget listWidget; // 执行命令 undoStack.push(new AddItemCommand(&listWidget, "New Item")); // 撤销 undoStack.undo(); // 重做 undoStack.redo();
2. Qt 的 QAction
(动作框架)
QAction
封装了用户触发的操作(如菜单项点击),可绑定到多个控件(菜单、工具栏按钮等),本质上是命令模式的轻量级应用。
核心思想:
QAction
将操作(如“复制”)封装为对象,通过信号triggered()
触发执行。- 可绑定快捷键、图标、文本等,统一管理状态(启用/禁用)。
示例代码:
QAction *copyAction = new QAction("Copy", this); copyAction->setShortcut(QKeySequence::Copy); connect(copyAction, &QAction::triggered, this, &MyWidget::copy); // 将动作添加到菜单和工具栏 menuBar()->addAction(copyAction); toolBar()->addAction(copyAction);
3. 自定义命令接口(通用实现)
若需更灵活的控制,可自定义命令基类,并手动管理命令队列。
实现步骤:
- 定义抽象命令接口。
- 实现具体命令类。
- 使用调用者(Invoker)触发命令。
示例代码:
// 1. 定义命令接口 class Command { public: virtual ~Command() {} virtual void execute() = 0; virtual void undo() = 0; }; // 2. 具体命令类:改变文本 class ChangeTextCommand : public Command { public: ChangeTextCommand(QLabel *label, const QString &newText) : m_label(label), m_oldText(label->text()), m_newText(newText) {} void execute() override { m_label->setText(m_newText); } void undo() override { m_label->setText(m_oldText); } private: QLabel *m_label; QString m_oldText; QString m_newText; }; // 3. 调用者(如按钮点击触发命令) QPushButton button("Change Text"); QLabel label("Original Text"); Command *cmd = new ChangeTextCommand(&label, "New Text"); QObject::connect(&button, &QPushButton::clicked, [cmd]() { cmd->execute(); });
4. 信号与槽的封装
通过将槽函数包装为命令对象,结合 QObject::connect
实现解耦。
示例场景:
- 将复杂操作封装为命令对象,通过信号触发执行。
- 使用
QSharedPointer
管理命令生命周期。
class CommandWrapper : public QObject { Q_OBJECT public: CommandWrapper(Command *cmd) : m_cmd(cmd) {} public slots: void execute() { m_cmd->execute(); } void undo() { m_cmd->undo(); } private: QSharedPointer<Command> m_cmd; }; // 连接信号到命令 CommandWrapper wrapper(new ChangeTextCommand(&label, "New Text")); QObject::connect(&button, &QPushButton::clicked, &wrapper, &CommandWrapper::execute);
5. 宏命令(Composite Command)
将多个命令组合成一个宏命令,实现批量操作。
- 示例代码:
class MacroCommand : public Command { public: void addCommand(Command *cmd) { m_commands.append(cmd); } void execute() override { for (auto cmd : m_commands) { cmd->execute(); } } void undo() override { for (auto it = m_commands.rbegin(); it != m_commands.rend(); ++it) { (*it)->undo(); } } private: QList<Command*> m_commands; }; // 使用示例 MacroCommand *macro = new MacroCommand; macro->addCommand(new Command1); macro->addCommand(new Command2); macro->execute();
总结
在 Qt 中应用命令模式的核心思想是:
- 封装操作:将请求封装为对象(如
QUndoCommand
)。 - 解耦调用者与接收者:通过信号/槽或直接调用命令接口。
- 支持扩展:通过组合或继承实现复杂逻辑(如宏命令)。
根据需求选择合适的方式,若需要撤销/重做,优先使用 QUndoStack
;若需简单解耦,可通过 QAction
或自定义命令类实现。