QT设计模式:命令模式

篇一、

基本概念
命令模式(Command Pattern)是一种行为设计模式,它通过将请求封装为具有公共接口的对象,从而允许将请求参数化、队列化、保存和执行。

使用场景
当需要将请求发送者与请求接收者解耦的场景
当需要将操作进行排队的场景
当操作需要支持撤销的场景
当要对操作过程记录日志的场景
当操作需要支持事务操作的场景
实现的类
Command类:是一个接口,定义了执行操作和撤销操作的方法。
ConcreteCommand类:具体的执行命令,他们需要实现Command接口,负责执行具体的操作。
Invoker类:发送命令的对象。它并不知道具体的命令实现细节,只是将命令对象发送给接收者来执行。
Receiver类:真正执行命令的角色,那些具体的命令引用它,让它完成命令的执行。
实现:QT文本编辑器

Command类
// 命令接口
class Command : public QUndoCommand
{
public:
    explicit Command(QTextEdit *editor) : m_editor(editor) {}
    virtual void execute() = 0;
 
protected:
    QTextEdit *m_editor;
};

Command 类是一个抽象类,继承自 QUndoCommand,定义了执行操作的接口方法 execute()。它提供了一个统一的接口,用于执行具体的操作。

ConcreteCommand类
// 具体的执行命令
class ConcreteCommand : public Command
{
public:
    ConcreteCommand(QTextEdit *editor, const QString &text)
        : Command(editor), m_text(text) {}
    //执行数据插入
    void execute() override
    {
        m_editor->textCursor().insertText(m_text);
    }
private:
    QString m_text;
};
 ConcreteCommand 类继承自 Command 类,负责具体的操作逻辑。在这个例子中,ConcreteCommand 类的任务是在文本编辑器中插入指定的文本。

Receiver类
// 文本编辑器接收者
class Receiver : public QObject
{
    Q_OBJECT
public:
    Receiver(QTextEdit *editor) : m_editor(editor) {}
public slots:
    void insertText(const QString &text)
    {
        m_editor->textCursor().insertText(text);
    }
private:
    QTextEdit *m_editor;
};


 Receiver 类其中包含了一个槽函数 insertText,负责在文本编辑器中插入文本。Receiver 类接收并执行命令,实际执行操作的地方。

Invoker实现(QUndoStack)
int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
 
    // 创建文本编辑器窗口
    QTextEdit textEdit;
    textEdit.setWindowTitle("Simple Text Editor");
    textEdit.show();
 
    // 创建撤销栈
    QUndoStack undoStack;
 
    // 创建文本编辑器接收者对象
    Receiver receiver(&textEdit);
 
    // 创建调用者对象并将其与撤销栈和接收者关联
    QUndoCommand *insertCommand = new ConcreteCommand(&textEdit, "Hello, World!");
    undoStack.push(insertCommand);
 
    // 执行命令
    undoStack.undo(); // 撤销插入操作
 
    return app.exec();
}

在 main 函数中,我们创建了一个 QUndoStack 对象 undoStack,它充当了调用者的角色。QUndoStack 类提供了管理命令的功能,包括执行和撤销命令。我们将具体的命令对象添加到 undoStack 中,并调用 undo() 方法执行命令。因此,undoStack 是命令模式中的调用者,负责调度命令的执行和撤销。
                 
原文链接:https://blog.csdn.net/buhuiCyvyan/article/details/138280467

———————————————— 

篇二、

撤销/重做框架(QUndoStack、QUndoCommand等类)
Qt的撤销/重做框架(QUndoStack、QUndoCommand等类)是命令模式的一种实现。在Qt的撤销/重做框架中,每个操作都被封装为一个QUndoCommand的子类对象。

以下是一个使用Qt的撤销/重做框架的程序示例:

#include <QApplication>
#include <QTextEdit>
#include <QUndoStack>
#include <QPushButton>
#include <QVBoxLayout>
#include <QUndoCommand>

class MyCommand : public QUndoCommand
{
public:
    MyCommand(QTextEdit *editor, const QString &text, QUndoCommand *parent = nullptr)
        : QUndoCommand(parent), m_editor(editor), m_text(text), m_oldText(editor->toPlainText()) {}

    void undo() override
    {
        m_editor->setPlainText(m_oldText);
    }

    void redo() override
    {
        m_editor->setPlainText(m_text);
    }

private:
    QTextEdit *m_editor;
    QString m_text;
    QString m_oldText;
};

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    QUndoStack stack;

    QTextEdit editor;
    QPushButton undoButton("Undo");
    QPushButton redoButton("Redo");

    QObject::connect(&undoButton, &QPushButton::clicked, &stack, &QUndoStack::undo);
    QObject::connect(&redoButton, &QPushButton::clicked, &stack, &QUndoStack::redo);

    QObject::connect(&editor, &QTextEdit::textChanged, [&]() {
        stack.push(new MyCommand(&editor, editor.toPlainText()));
    });

    QVBoxLayout layout;
    layout.addWidget(&editor);
    layout.addWidget(&undoButton);
    layout.addWidget(&redoButton);

    QWidget window;
    window.setLayout(&layout);
    window.show();

    return app.exec();
}


在上述示例中,MyCommand 是 QUndoCommand 的子类。每次 QTextEdit 的文本改变时,就会创建一个新的 MyCommand 对象并将其压入 QUndoStack。当点击 "Undo" 按钮时,就会撤销栈顶的命令;当点击 "Redo" 按钮时,就会重做栈顶的命令。

在这个例子中,相关的类扮演的角色如下:

命令(Command):QUndoCommand是命令接口。它定义了执行(redo)和撤销(undo)的方法。
具体命令(ConcreteCommand):MyCommand是具体的命令。它是 QUndoCommand的子类,实现了 redo和undo方法。
命令接收者(Receiver):QTextEdit是命令的接收者。它是执行命令的对象,MyCommand的 redo和 undo方法都是操作 QTextEdit。
命令发起者(Invoker):QUndoStack是命令的发起者。它负责调用和存储命令。QPushButton 实际上也扮演了命令发起者的角色,因为它们是触发执行或撤销命令的实际用户界面元素。
客户端(Client):在这个例子中,main 函数就是客户端。它创建了应用程序,包括命令接收者(QTextEdit)、命令发起者(QUndoStack 和 QPushButton)、并在 QTextEdit的文本变化时创建具体命令(MyCommand)。
下面给出相关的类在Qt源码中的实现。同样,这里隐去了很多与命令模式无关的细节,但对理解命令模式应该是足够的。

class QUndoCommand
{
public:
    virtual ~QUndoCommand() {}
    virtual void undo() = 0;
    virtual void redo() = 0;
};

class QUndoStack
{
public:
    void push(QUndoCommand *cmd)
    {
        m_stack.push(cmd);
        cmd->redo();
    }

    void undo()
    {
        if (!m_stack.isEmpty()) {
            QUndoCommand *cmd = m_stack.pop();
            cmd->undo();
            m_undoStack.push(cmd);
        }
    }

    void redo()
    {
        if (!m_undoStack.isEmpty()) {
            QUndoCommand *cmd = m_undoStack.pop();
            cmd->redo();
            m_stack.push(cmd);
        }
    }

private:
    QStack<QUndoCommand*> m_stack;
    QStack<QUndoCommand*> m_undoStack;
};


MyCommand类我们已经在前面实现了,这里不再重复。对于命令的接收者QTextEdit,我们也不需要关注它的源码,因为它的功能就是一个文本编辑器,我们只需要知道它提供了setPlainText()和toPlainText()等函数供我们在MyCommand中使用就可以了。

需要注意的是,在标准的命令模式中,通常只有一个存储命令对象的容器,可以是是队列或栈,也可以仅仅是只是单个命令对象(如我们一开始给出的命令模式的示例)。然而,在实现撤销/重做功能时,通常需要两个栈结构。

在Qt的QUndoStack中,主栈用于存储执行过的命令,当调用undo()方法时,会从主栈中弹出命令并执行其undo操作,同时该命令会被压入撤销栈。撤销栈用于存储撤销过的命令,当调用redo()方法时,会从撤销栈中弹出命令并执行其redo操作,同时该命令会被压回主栈。这样的设计使得QUndoStack能够按正确的顺序执行和撤销命令,同时还能在撤销命令后重新执行它们。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值