命令模式—C++实现撤消重做

15 篇文章 0 订阅
3 篇文章 0 订阅

Command

结构
这里写图片描述

意图
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。

适用性
1、抽象出待执行的动作以参数化某对象,你可用过程语言中的回调(c a l l b a c k )函数表达这种参数化机制。所谓回调函数是指函数先在某处注册,而它将在稍后某个需要的时候被调用。C o m m a n d 模式是回调机制的一个面向对象的替代品。
2、在不同的时刻指定、排列和执行请求。一个C o m m a n d 对象可以有一个与初始请求无关的生存期。如果一个请求的接收者可用一种与地址空间无关的方式表达,那么就可将负责该请求的命令对象传送给另一个不同的进程并在那儿实现该请求。
3、支持取消操作。C o m m a n d 的E x c u t e 操作可在实施操作前将状态存储起来,在取消操作时这个状态用来消除该操作的影响。C o m m a n d 接口必须添加一个U n e x e c u t e 操作,该操作取消上一次E x e c u t e 调用的效果。执行的命令被存储在一个历史列表中。可通过向后和向前遍历这一列表并分别调用U n e x e c u t e 和E x e c u t e 来实现重数不限的“取消”和“重做”。
4、支持修改日志,这样当系统崩溃时,这些修改可以被重做一遍。在C o m m a n d 接口中添加装载操作和存储操作,可以用来保持变动的一个一致的修改日志。从崩溃中恢复的过程包括从磁盘中重新读入记录下来的命令并用E x e c u t e 操作重新执行它们。
5、用构建在原语操作上的高层操作构造一个系统。这样一种结构在支持事务( t r a n s a c t i o n )的信息系统中很常见。一个事务封装了对数据的一组变动。C o m m a n d 模式提供了对事务进行建模的方法。C o m m a n d 有一个公共的接口,使得你可以用同一种方式调用所有的事务。同时使用该模式也易于添加新事务以扩展系统。

示例
命令(command)模式最常用用法之一就是用作实现软件中的撤销重做功能。这里是本人在工作中遇到的用命令模式实现撤销重做功能的例子,C++实现。
首先,需要定义一个Command基类:

class Command
{
public:
    Command(void);
    virtual ~Command(void){};

public:
    virtual void redo()=0;//重做

    virtual void undo()=0;//撤销
};

有了基类,我们就可以根据自己需要定义不同元操作的命令子类。
1、修改命令类

//修改命令类
class CModifyCommand :
    public Command
{
public:
    CModifyCommand(CBaseShape* shape,const QPointF &ptOffSet);
    virtual ~CModifyCommand(void);
public:
    virtual void redo();
    virtual void undo();
private:
    CBaseShape* m_shape;
    QPointF m_ptOffSet;
};
#include "ModifyCommand.h"
CModifyCommand::CModifyCommand( CBaseShape* shape,const QPointF &ptOffSet )
{
    m_shape = shape;
    m_ptOffSet = ptOffSet;
}
CModifyCommand::~CModifyCommand(void)
{
}

void CModifyCommand::redo()
{
    m_shape->Excute(m_ptOffSet);
}

void CModifyCommand::undo()
{
    m_shape->Excute(-m_ptOffSet);
}

2、批处理命令类

#include "command.h"
#include <vector>

using namespace std;
//批处理命令类
class CBatchCommand :
    public Command
{
public:
    CBatchCommand(void);
    virtual ~CBatchCommand(void);
public:
    //添加子命令
    void AddCommand(Command* cmd);
    //执行子命令重做函数
    virtual void redo();
    //执行子命令撤销函数
    virtual void undo();
private:
    vector<Command*> m_vecCmds;//存储子命令
};

.cpp实现

#include "BatchCommand.h"
CBatchCommand::CBatchCommand(void)
{
}

CBatchCommand::~CBatchCommand(void)
{
    for (int i=0;i<m_vecCmds.size();++i)
    {
        if (NULL != m_vecCmds[i])
        {
            delete m_vecCmds[i];

            m_vecCmds[i] = NULL;
        }
    }

    m_vecCmds.clear();
}

void CBatchCommand::redo()
{
    for (int i=0;i<m_vecCmds.size();++i)
    {
        m_vecCmds[i]->redo();
    }
}

void CBatchCommand::undo()
{
    for (int i=0;i<m_vecCmds.size();++i)
    {
        m_vecCmds[i]->undo();
    }
}

void CBatchCommand::AddCommand( Command* cmd )
{
    m_vecCmds.push_back(cmd);
}

定义了命令类,我们还要一个类来管理存储我们每次操作所产生的各种命令。

CommandManager类,这里我用了两个容器来存储命令,一个存储撤销命令,一个存储重做命令。

#include "Command.h"
#include <vector>

using namespace std;
//命令管理类
class CommandManager
{
public:
    CommandManager(void);
    virtual ~CommandManager(void);

public:
    //存储命令
    virtual void StoreCommand(Command* cmd);
    //清除所有命令
    virtual void ClearAllCommand();
    //清除重做命令
    virtual void ClearRedoCommand();
    //执行撤销命令
    virtual void Undo() = 0;
    //执行重做命令
    virtual void Redo() = 0;
public:
    vector<Command*> m_vecRedo;
    vector<Command*> m_vecUndo;
};
#include "CommandManager.h"

CommandManager::CommandManager(void)
{
}

CommandManager::~CommandManager(void)
{
}

void CommandManager::StoreCommand( Command* cmd )
{
    m_vecUndo.push_back(cmd);
    ClearRedoCommand();//添加新命令时,清除重做命令
}

void CommandManager::ClearAllCommand()
{
    for (int i = 0;i<m_vecRedo.size();++i)
    {
        if (NULL != m_vecRedo[i])
        {
            delete m_vecRedo[i];

            m_vecRedo[i] = NULL;
        }
    }

    for (int i = 0;i<m_vecUndo.size();++i)
    {
        if (NULL != m_vecUndo[i])
        {
            delete m_vecUndo[i];

            m_vecUndo[i] = NULL;
        }
    }

    m_vecRedo.clear();
    m_vecUndo.clear();
}

void CommandManager::ClearRedoCommand()
{
    for (int i = 0;i<m_vecRedo.size();++i)
    {
        if (NULL != m_vecRedo[i])
        {
            delete m_vecRedo[i];

            m_vecRedo[i] = NULL;
        }
    }

    m_vecRedo.clear();
}

CommandHistoryManager 类,我这里设计成了单例类,方便调用。

#include "commandmanager.h"

class CommandHistoryManager :
    public CommandManager
{
public:
    CommandHistoryManager(void);
    virtual ~CommandHistoryManager(void);
public:
    static CommandHistoryManager *GetInstance()
    {
        if (NULL == m_pCmdHistoryManager)
        {
            m_pCmdHistoryManager = new CommandHistoryManager();
        }

        return m_pCmdHistoryManager;
    }
    static void ReleaseInstance();
    virtual void Undo();
    virtual void Redo();
private:
    static CommandHistoryManager* m_pCmdHistoryManager;
};

实现Undo(),Redo()函数

#include "CommandHistoryManager.h"

CommandHistoryManager * CommandHistoryManager::m_pCmdHistoryManager = NULL;
CommandHistoryManager::CommandHistoryManager(void)
{
}

CommandHistoryManager::~CommandHistoryManager(void)
{
}

void CommandHistoryManager::Undo()
{
    if ( m_vecUndo.size() <= 0 ) return;
    Command* cmd = m_vecUndo.at(m_vecUndo.size()-1);
    cmd->undo();
    m_vecUndo.pop_back();
    m_vecRedo.push_back(cmd);
}

void CommandHistoryManager::Redo()
{
    if ( m_vecRedo.size() <= 0 ) return;
    Command* cmd = m_vecRedo.at(m_vecRedo.size()-1);
    cmd->redo();
    m_vecRedo.pop_back();
    m_vecUndo.push_back(cmd);
}

void CommandHistoryManager::ReleaseInstance()
{
    if (NULL != m_pCmdHistoryManager)
    {
        delete m_pCmdHistoryManager;

        m_pCmdHistoryManager = NULL;
    }
}

使用方法

CModifyCommand 命令使用方式

CModifyCommand *mCmd = new CModifyCommand(this,ptOffset);
CommandHistoryManager::GetInstance()->StoreCommand(mCmd);

CBatchCommand 命令使用方式

CBatchCommand *bCmd = new CBatchCommand();
QList<CMyPointShape*>::iterator it;
for (it = m_myPointList.begin();it != m_myPointList.end();++it)
{
    CModifyCommand *mCmd = new CModifyCommand(*it,ptOffset);
    bCmd->AddCommand(mCmd);
}
CommandHistoryManager::GetInstance()->StoreCommand(bCmd);

最后

以上只是自己的一种实现方式,可能有不妥之处,还望指正。实现的方法有很多,此处仅供参考。

  • 3
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
QTableView 是 Qt 框架的一种表格视图控件,如果想要支持撤销重做操作,可以在模型添加相应的实现。以下是一个基于 C++ 的示例代码,演示如何在 QTableView 支持撤销重做操作。 首先,我们需要定义一个基于 QStandardItemModel 的自定义模型类,这个类需要继承自 QStandardItemModel,同时支持撤销重做操作。在这个自定义模型类,我们可以创建一个 QVector 存储所有的操作记录,并使用 QUndoStack 类来实现撤销重做操作。 ```cpp class CustomTableModel : public QStandardItemModel { Q_OBJECT public: CustomTableModel(int rows, int columns, QObject *parent = nullptr); ~CustomTableModel(); void setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; bool undo(); bool redo(); private: QVector<QPair<QModelIndex, QVariant>> m_undoStack; QVector<QPair<QModelIndex, QVariant>> m_redoStack; QUndoStack* m_undoRedoStack; private slots: void onStackChanged(bool canUndo, bool canRedo); }; ``` 在构造函数,我们初始化了撤销重做堆栈和 QUndoStack 实例。在 setData 函数,我们记录了每次修改的操作记录。在 undo 和 redo 函数,我们通过使用 QUndoStack 实例的 undo 和 redo 函数实现撤销重做操作。在 onStackChanged 函数,我们更新了撤销重做操作的可用性状态。 ```cpp CustomTableModel::CustomTableModel(int rows, int columns, QObject *parent) : QStandardItemModel(rows, columns, parent) { m_undoRedoStack = new QUndoStack(this); connect(m_undoRedoStack, &QUndoStack::canUndoChanged, this, &CustomTableModel::onStackChanged); connect(m_undoRedoStack, &QUndoStack::canRedoChanged, this, &CustomTableModel::onStackChanged); } CustomTableModel::~CustomTableModel() { delete m_undoRedoStack; } void CustomTableModel::setData(const QModelIndex &index, const QVariant &value, int role) { QStandardItemModel::setData(index, value, role); QPair<QModelIndex, QVariant> data(index, value); m_undoStack.append(data); m_redoStack.clear(); QUndoCommand* command = new QUndoStack::CustomUndoCommand(m_undoStack, m_redoStack, this); m_undoRedoStack->push(command); } bool CustomTableModel::undo() { if (m_undoRedoStack->canUndo()) { m_undoRedoStack->undo(); return true; } return false; } bool CustomTableModel::redo() { if (m_undoRedoStack->canRedo()) { m_undoRedoStack->redo(); return true; } return false; } void CustomTableModel::onStackChanged(bool canUndo, bool canRedo) { emit undoAvailable(canUndo); emit redoAvailable(canRedo); } ``` 最后,我们需要在窗口创建一个 QTableView 实例,并将自定义模型类设置为它的数据模型。我们还需要为撤销重做操作创建相应的槽函数,并将它们与窗口的按钮信号连接。 ```cpp class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private: Ui::MainWindow *ui; CustomTableModel* m_model; private slots: void onUndo(); void onRedo(); }; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); m_model = new CustomTableModel(3, 2, this); ui->tableView->setModel(m_model); connect(ui->undoButton, &QPushButton::clicked, this, &MainWindow::onUndo); connect(ui->redoButton, &QPushButton::clicked, this, &MainWindow::onRedo); } MainWindow::~MainWindow() { delete ui; } void MainWindow::onUndo() { m_model->undo(); } void MainWindow::onRedo() { m_model->redo(); } ``` 通过这个示例代码,我们可以在 QTableView 实现撤销重做操作。当用户对表格的单元格进行修改时,可以将这个修改操作记录下来,并将操作记录添加到撤销堆栈。当用户点击撤销按钮时,我们可以从撤销堆栈取出最后一次操作,将其还原到表格,并将其添加到重做堆栈。当用户点击重做按钮时,我们可以从重做堆栈取出最后一次操作,并将其添加到撤销堆栈

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值