撤销和重做(Undo和Redo)的C++完美实现(4)

#if 0

在上一篇文章中已经成功的将三个基本操作封装为三个基本命令,同时也将一个复合
操作封装成了一个复合命令,另外也给出了简单的使用代码;从代码中可以看出,撤销和
重做的过程还是比较晦涩,用户为了表达撤销和重做的过程还需要编写很多的额外代码,
而且这种代码也没有很直接的表达撤销和重做的思想。为了更好的表达撤销和重做机制,
并且更加简化客户端代码的书写,在本章中将会对前一章中给出的代码进行更深入的封装
,让客户端编写的代码尽可能的少,同时也能构更加直接的表达撤销和重做的意思。

总的说来,在本章中将要达到下列目标:

(1)处理更多的对象类型,前面只是处理了一种类型(Object)。从本质上来说,处理
多个数据类型和处理一个数据类型是一样的,但是对于方便使用该撤销和重做框架来说,
给出一个示例将会是读者更加直接的了解到如何使用该框架的方便的实现自己的撤销和重
做能力。另外从方便使用的角度来说,本章中必须给出一个惯例,并且在这个惯例的基础
上,实现撤销和重做功能。实际上在本文中介绍的撤销和重做框架还天生具备了序列化能
力,这可是一个非常不错的副产品哦:)关于序列化的原理,可以参见本人的其它相关文档。

(2)撤销和重做过程在代码中表达得更加直观。从前一章的示例代码中可以看出,撤销
和重做的功能虽然可以实现了,但是也要注意撤销和重做的表现并不直观,直接看到代码
,阅读代码的人并不能够很容易的了解代码的意义;所以,本章中会改进这种代码,使得
阅读代码就像阅读文档一样,使代码更直接的表达撤销和重做的意义。

(3)实现任意次的撤销和重做。前面的撤销和重做次数默认是无限的,虽然默认的情况
下是无限次的撤销和重做,但是客户端有时候可能因为种种原因而不想使撤销和重做的步
骤太多,因而在本章中还会给出如何实现有限次的撤销和重做方案。

现在为了突出本章的重点,需要将前一章中的代码保存到一个独立的源文件中,从设
计模式的角度来说,我们主要使用的模式是命令模式,所以这个保存的文件的文件名就命
名为“command.h”。为了使本文以及后续的章节都使用到这个头文件,在本文的末尾将
附带给出该头文件的内容。

下面开始讨论本章开头所提出的三个问题了。对于问题(1)文中将会给出两个比较直接
的数据类型:矩形类(Rectangle)和圆形类(Circle)。对于问题(2)文中将会更进一步的封
装前面的使用方式,最后一个问题,文中将会给出一个使用惯例,是可以封装成为头文件
的,这在后面讨论。

#endif
#include <limits>//numeric_limit<size_t>::max()获得最大整数
#include "command.h"

namespace pandaxcl{
class undo_queue:public macro{};
class redo_queue:public macro{};
class center:public undo_queue,public redo_queue
{
private:
//该变量记录的是该程序允许撤销和重做步骤的最大次数,当这个数字非常大的时候
//可以认为撤销步骤是无限次的。
size_t _limit;
public:
typedef redo_queue redo_type;
typedef undo_queue undo_type;
typedef undo_type history_type;
//默认是无限次撤销和重做步骤
center():_limit(std::numeric_limits<size_t>::max()){}
//程序退出的时候应该清理所有命令使用的内存空间
virtual~center()
{
undo_type::free();//释放撤销队列
redo_type::free();//释放重做队列
}
public:
//获得该中心的最大可撤销步骤数量
size_t limit()
{
return _limit;
}
//设置该中心的最大可撤销步骤数量之后再清理过期的命令
void limit(size_t Limit)
{
_limit=Limit;
relimit();
}
//根据当前的最大可撤销步骤数量清理过期的命令
void relimit()
{
typedef std::list<command*> QT;
if(_limit >= undo_type::size())return;
while(_limit < undo_type::size())
{
command *pCmd = static_cast<command*>(undo_type::front());
undo_type::pop_front();//将过期的可撤销命令从命令历史中清理掉
delete pCmd;//然后释放这个命令所占用的资源
}
}

public:
//一次撤销一步操作
void undo()
{
if (!undo_type::empty()) {
undo_type::back()->execute(false);
redo_type::push_back(undo_type::back());
undo_type::pop_back();
}
}
//一次重做一步操作
void redo()
{
if (!redo_type::empty()) {
redo_type::back()->execute(true);
undo_type::push_back(redo_type::back());
redo_type::pop_back();
}
}
//一次撤销多步操作
void undo(int number)
{
for(int i=0;i<number;i++) {
if(undo_type::empty())break;
else undo();
}
}
//一次重做多步操作
void redo(int number)
{
for(int i=0;i<number;i++) {
if(redo_type::empty())break;
else redo();
}
}
//执行并记录新的命令函数
void execute(command*pCmd)
{
assert(pCmd!=NULL);
pCmd->execute(true);
record(pCmd);//将新命令记录到命令历史记录中
relimit();//到了这里需要针对有限次的撤销和重做进行特殊的处理
}
//向历史记录中添加新的命令,并不会执行所添加的命令
void record(command*pCmd=NULL)
{
//向历史记录中添加新的操作命令
history_type::record(pCmd);
//每当记录新的可撤销命令的时候需要将可重做命令队列清空
redo_type::free();
}
//这个函数和上面的record(NULL)函数一起组合实现复合命令的创建过程
void stop()
{
typedef command IT;
typedef macro QT;
history_type &H=static_cast<history_type&>(*
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值