1. 程式人生 > >命令模式—C++實現撤消重做

命令模式—C++實現撤消重做

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);

最後

以上只是自己的一種實現方式,可能有不妥之處,還望指正。實現的方法有很多,此處僅供參考。