命令模式—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);

最后

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值