C++Commandモードで簡単にUndo&Redo機能を実現

21874 ワード

変換元:https://www.cnblogs.com/TianFang/archive/2013/05/19/3086820.html多くのGUIプログラムでは、ユーザーにとって非常に友好的な「取り消し&やり直し」機能が提供されています.この機能をC#で実現する方法を簡単に紹介します.
Undo&Redo機能を実装する基本モデルは、次のようにステップごとにコマンドオブジェクトとして保存される取り消し機能付きコマンドモードです.
interface Icommand
{
    void Do();
    void Undo();
}

ここでDo関数実行機能,Undo関数ロールバック機能.これによりコマンドを実体化し,コマンドオブジェクトを保存し,取り消しが必要な場合はUndo関数,やり直し時はDo関数を実行すればよい.
この基本的な考え方があれば、以下は実現の詳細です.
  • は2つのStackを申請してコマンドオブジェクトを保存する:UndoStackとRedoStack
  • コマンドを実行する場合は、コマンドをCommandオブジェクトにシーケンス化し、Doメソッドを実行し、UndoStackに格納し、RedoStack
  • をクリアする.
  • コマンドを取り消すと、UndoStackからコマンドを取り出し、Undoメソッドを実行し、RedoStack
  • に格納する.
  • やり直しコマンドの場合は、RedoStackからコマンドを取り出すDoメソッドを実行し、UndoStack
  • に格納する.
    シンプルな実装:
    #ifndef _COMMAND_HPP
    #define _COMMAND_HPP
     
    #include 
    #include 
    #include 
     
    class Command
    {
    public:
    	Command(){}
    	virtual ~Command(){}
     
    	virtual void Execute() = 0;
    };
     
     
    class InputCommand : public Command
    {
    	public:
     
    		InputCommand( const std::string &input )
    		{
    			mInput = input;
    		}
    		~InputCommand()
    		{}
     
    		void Execute()
    		{
    			printf( "current string: %s
    "
    , mInput.c_str() ); } private: std::string mInput; }; class Commander { public: Commander( Command *pCmd ) { // mUndo.push( pCmd ); } ~Commander() { while( false == mUndo.empty() ) { delete mUndo.top(); mUndo.pop(); } while( false == mRedo.empty() ) { delete mRedo.top(); mRedo.pop(); } } void ExecuteCommand( Command *pCmd ) { pCmd->Execute(); mUndo.push( pCmd ); } void Undo() { if( mUndo.size() < 2 ) { // printf( "undo failed.
    "
    ); return; } printf( "undo:
    "
    ); // auto pCmd = mUndo.top(); // mRedo.push( pCmd ); // mUndo.pop(); // pCmd = mUndo.top(); pCmd->Execute(); } void Redo() { if( mRedo.empty() ) { // printf( "redo failed.
    "
    ); return; } printf( "redo:
    "
    ); auto pCmd = mRedo.top(); pCmd->Execute(); mRedo.pop(); mUndo.push( pCmd ); } private: std::stack< Command* > mUndo; std::stack< Command* > mRedo; }; #endif
    #include "Command.hpp"
     
    //       ,           undo redo  
    //      undo redo                
    //          , :   undo   redo           
    //    ,       undo    redo          redo    
    //                    ,    redo
    int main()
    {
    	//             ,                
    	Commander *p = new Commander( new InputCommand( "[empty]" ) );
     
    	//  1234
    	p->ExecuteCommand( new InputCommand( "1234" ) );
    	//  567
    	p->ExecuteCommand( new InputCommand( "567" ) );
    	//  xxx
    	p->ExecuteCommand( new InputCommand( "xxx" ) );
     
    	//    undo     567
    	p->Undo();
    	//    undo     1234
    	p->Undo();
     
    	//undo          insert    1234
    	p->ExecuteCommand( new InputCommand( "insert" ) );
     
    	//    undo     1234
    	p->Undo();
     
    	//    undo         [empty]
    	p->Undo();
     
    	//      undo       
    	p->Undo();
     
    	//    redo     1234
    	p->Redo();
    	//    redo     insert
    	p->Redo();
    	//    redo     567
    	p->Redo();
     
    	//redo          insert again    567
    	p->ExecuteCommand( new InputCommand( "insert again" ) );
     
    	//    undo    567
    	p->Undo();
     
    	//    redo     insert again
    	p->Redo();
     
    	//    redo     xxx
    	p->Redo();
     
    	//      redo      
    	p->Redo();
    	delete p;
    	return 0;
    }