使用设计模式实现Undo,Redo框架
来源:互联网 发布:淘宝订单可以拆分吗 编辑:程序博客网 时间:2024/04/27 20:55
编辑器程序少不了要支持undo, redo功能,如何实现?本文就是参考了设计模式中给出的思路实现了一个。
这里主要用到了两个模式:命令(command)模式, 备忘录(memento)模式
所谓编辑,功能上可以分成3个原子操作:添加新内容,编辑已有内容,删除已有内容, 因此编辑功能3个command实现,AddCommand, EditCommand, DeleteCommand. 这3个命令实现了相同的接口do, undo.
先说一下代码的风格,自从使用WTL,就喜欢上了WTL的代码风格,本文就是仿照WTL风格写的.
先看一下Command的接口:
- class CCommand
- {
- public:
- virtual ~CCommand();
- public:
- virtual bool Do() = 0;
- virtual bool Undo() = 0;
- virtual bool CanUndo() = 0;
- };
AddCommand, EditCommand, DeleteCommand都实现了这个接口类。
下面WTL风格代码开始了:
先留个空架子
- template<class TBase/*where TBase : public CCommand*/>
- class CCommandImplBase : public TBase
- {
- //先空着,以后留着扩展
- };
下面是主要实现:用到了event, 当Command执行时会触发event,你可以在时间里做一些诸如试图更新, 所谓event,跟C#里的事件相似,感兴趣的可以看我前面的文章用C++模拟C#事件机制。
- template< class T, class TBase = CCommand, class TTraits = CommandTraits<T> >
- class CCommandImpl : public CCommandImplBase<TBase>
- {
- public:
- typedef TTraits::CmdEvent CmdEvent;
- typedef CmdEvent::EventHandler EventHandler;
- public:
- CCommandImpl(typename CmdEvent::EventArgs/* const& */args) : m_Args(args)
- {
- #if (_MSC_VER >= 1300)
- //COMPLIE_ASSERT(IsPointer(args));
- bool bRet = TypeTraits<CmdEvent::EventArgs>::IsPointer();
- #endif
- }
- virtual ~CCommandImpl()
- {
- }
- public:
- void RegisterDoHandler(typename CmdEvent::EventHandler/* const*/& handler)
- {
- m_DoEvent += handler;
- }
- void UnRegisterDoHandler(typename CmdEvent::EventHandler/* const*/& handler)
- {
- m_DoEvent -= handler;
- }
- void RegisterUndoHandler(typename CmdEvent::EventHandler/* const*/& handler)
- {
- m_UndoEvent += handler;
- }
- void UnRegisterUndoHandler(typename CmdEvent::EventHandler/* const*/& handler)
- {
- m_UndoEvent -= handler;
- }
- public:
- virtual bool Do()
- {
- if (!DoCommand())
- {
- return false;
- }
- typename CmdEvent::ReturnValue ret = m_DoEvent(GetEventArgs());
- return true;
- }
- virtual bool Undo()
- {
- if (!CanUndo())
- {
- return false;
- }
- if (!UndoCommand())
- {
- return false;
- }
- typename CmdEvent::ReturnValue ret = m_UndoEvent(GetEventArgs());
- return true;
- }
- virtual bool CanUndo()
- {
- return TTraits::CanUndo;
- }
- protected:
- virtual bool DoCommand() = 0;
- virtual bool UndoCommand(){return true;}
- // implementations
- protected:
- typename CmdEvent::EventArgs /*const&*/ GetEventArgs() const
- {
- return m_Args;
- }
- private:
- CmdEvent m_DoEvent;
- CmdEvent m_UndoEvent;
- typename CmdEvent::EventArgs m_Args;
- };
这里用到了CommandTraits模板,所谓Traits,能在编译时提供一些类型信息,感兴趣的同学可以去侯捷的网站看看Traits: 類型的else-if-then機製,相关的文章好多。
CommandTraits是一系列模板特化, 每个Traits包含一个event类型, 这在前面已经用到了.
- template<class T>
- struct CommandTraits;
- //Common Command Traits
- template<>
- struct CommandTraits<CAddCommand>
- {
- typedef Event<bool, CAddEventArgs const*> CmdEvent;
- enum { CanUndo = true};
- };
- template<>
- struct CommandTraits<CEditCommand>
- {
- typedef Event<bool, CEditEventArgs const*> CmdEvent;
- enum { CanUndo = true};
- };
- template<>
- struct CommandTraits<CDeleteCommand>
- {
- typedef Event<bool, CDeleteEventArgs const*> CmdEvent;
- enum { CanUndo = true};
- };
在接下来就是
AddCommand, EditCommand, DeleteCommand 的实现了可以使用备忘录模式将每个命令需要保存的数据(CmdEvent::EventArgs)提取出来,可以用一组get,set操作实现状态的提取和保存,具体保存的内容因项目而易,很简单,不罗嗦了:
- class CAddCommand : public CCommandImpl<CAddCommand>
- {
- public:
- CAddCommand(CAddEventArgs const* pArgs);
- virtual ~CAddCommand();
- protected:
- virtual bool DoCommand()
- {
- //这里写实际的代码,比如可以将输入的内容存起来. 可以考虑用备忘录模式实现下同,就不罗列代码了
- }
- virtual bool UndoCommand()
- {
- //这里写实际的代码,比如可以将输入的内容删掉. 下同,就不罗列代码了
- }
- };
- class CEditCommand : public CCommandImpl<CEditCommand>
- {
- public:
- CEditCommand(CEditEventArgs const* pArgs);
- virtual ~CEditCommand();
- protected:
- virtual bool DoCommand();
- virtual bool UndoCommand();
- };
- class CDeleteCommand : public CCommandImpl<CDeleteCommand>
- {
- public:
- CDeleteCommand(CDeleteEventArgs const* pArgs);
- virtual ~CDeleteCommand();
- protected:
- virtual bool DoCommand();
- virtual bool UndoCommand();
- };
所有的命令都全了,可是还要把所有命令按顺序存储,Undo的时候这按这个相反的顺序拿出来就可以了,用std::stack正好,下面实现了一个CCommandManager , 就是用来管理命令的:
- template< class TCommand /*where TCommand : public CCommand*/ >
- class CCommandManager
- {
- public:
- CCommandManager()
- :m_UndoStack()
- {
- }
- virtual ~CCommandManager(){}
- public:
- bool Excecute(TCommand* cmd)
- {
- if (!cmd->Do())
- {
- return false;
- }
- if (cmd->CanUndo())
- {
- m_UndoStack.push(cmd);
- }
- return true;
- }
- bool ReExecute()
- {
- TCommand* cmd = m_RedoStack.top();
- if (!Excecute(cmd))
- {
- return false;
- }
- m_RedoStack.pop();
- return true;
- }
- bool UnExecute()
- {
- TCommand* cmd = m_UndoStack.top();
- if (!cmd->Undo())
- {
- return false;
- }
- m_UndoStack.pop();
- m_RedoStack.push(cmd);
- return true;
- }
- void Reset()
- {
- if (!m_UndoStack.empty())
- {
- TCommand* pCmd = m_UndoStack.top();
- m_UndoStack.pop();
- delete pCmd;
- }
- }
- private:
- std::stack<TCommand*> m_UndoStack;
- std::stack<TCommand*> m_RedoStack;
- };
CCommandManager<CCommand>还可以实现为单件模式,这样可以提供一个全局访问点,这里因为篇幅关系不实现了。
至此整个Undo,Redo的框架已经实现完了,下面介绍如何使用
- //1. 定义命令参数并填写需要的值
- CAddEventArgs* pArgs = new CAddEventArgs(......);
- //2. 定义Command, 并把相应的参数填进去.
- CAddCommand* cmd = new CAddCommand(pArgs);
- //3. 定义事件响应函数.
- CAddCommand::EventHandler Handler(GetView(), &CKeyinToolView::OnEditPOI);
- //4. 把事件响应函数注册到event
- cmd ->RegisterDoHandler(Handler);
- //5. 现在放心的运行命令,运行成功则CommandManager会信息保存到Undo栈中,并触发event处理函数.
- GetCommandManager()->Excecute(cmd)。
- //6. 在需要Undo的时候只需这样
- GetCommandManager()->UnExcecute()。
- //7. 在需要Redo的时候只需这样
- GetCommandManager()->ReExcecute()。
- 使用设计模式实现Undo,Redo框架
- Undo/Redo框架实现
- 设计模式:Command模式 实现无限次的undo、redo
- 设计模式——命令模式实现undo、redo
- C++实现Undo和Redo框架(命令模式)
- 命令模式实现 Undo Redo
- C++下利用Command设计模式实现undo和redo
- 用 Command设计模式实现 Undo和Redo 功能
- Undo/Redo框架的一种实现
- Command模式实现撤销重做(Undo/Redo)
- Command模式实现撤销重做(Undo/Redo)
- 设计模式:利用Command模式实现无限次数的Undo/Redo功
- 设计模式:利用Command模式实现无限次数的Undo/Redo功能
- Undo/Redo设计(一)
- Undo/Redo设计(二)
- Undo/Redo设计(三)
- Undo/Redo设计(四)
- Undo/Redo设计(五)
- 手机号码黑名单
- C语言宏定义使用技巧
- WordPress简介
- good example for web automation and exe automation
- 几个画图函数
- 使用设计模式实现Undo,Redo框架
- sharepoint 的配置
- 控制台wcf无需iis解决穿域文件
- 使应用程序只能运行一个实例
- leadsoft成套电器报价系统Winner解决方案
- IE和Firefox在css,JavaScript方面的兼容性
- 运用Delphi编写Windows NT中服务程序
- 软件设计原则
- 用div做的细线表格效果