说明: 本文只主要讲述一些设计模式在二维图形程序里的应用.不涉及到二维图形程序的算法.
最近很忙,没时间写。希望看到的朋友别介意,有时间我会好好整理一下的,希望对正在做这方面的朋友有用。
本文讲述命令模式的一个实现,此实现是笔者项目中实际用到的.
按照设计模式的命令模式,我们先定义一个基类,如下:
// 命令类型
enum cmd_type { type_no, type_create, type_modify, type_delete, type_micro, type_composite, };
// 命令类的抽象类
class command
{
public:
virtual ~command(void)
{
}
virtual void execute(void) = 0;
virtual void un_execute(void) = 0;
cmd_type type(void) { return _type; }
bool need_destory(bool bdestory) { return _destory = bdestory; }
protected:
command(void)
{
_type = type_no;
_destory = false;
}
protected:
cmd_type _type; // 命令类型
//string _cmdname; // 命令名字
bool _destory; // 是否删除命令里的对象
};
command是一个抽象类,提供具体命令的基类。execute和un_execute表示命令的执行与反执行(对应redo和undo)。有两个数据成员,_destory表示在command析构时是否需要删除保存在命令里的对象指针。_type表示命令的类型,取值为cmd_type 中的一个。
也许你不理解为什么需要cmd_type以及_destory,后面我想我会解释到的,不信,往下看:)
在实际中,往往需要将多个命令组合成一个命令,以实现多步操作一步撤消。下面这个类正是你所需要的。
为实现简单,该类选择直接从deque派生。另外一种方法是在类macro_command里申明一个deque的变量,再提供push,pop等操作。
class macro_command
: public command
, public deque<command*>
{
public:
micro_command(void)
{
_destory = false; // 此处的_destory为true时,表示清空redo列表,为false时表示清空undo列表
_type = type_micro;
}
virtual ~micro_command(void)
{
for(iterator ite = begin(); ite != end(); ++ite)
{
if((*ite)->type() == type_create)
(*ite)->need_destory(_destory);
else if((*ite)->type() == type_delete)
(*ite)->need_destory(!_destory);
else if((*ite)->type() == type_micro)
(*ite)->need_destory(_destory);
delete *ite;
}
}
public:
virtual void execute(void) { for_each(begin(), end(), mem_fun(&command::execute)); }
virtual void un_execute(void) { for_each(begin(), end(), mem_fun(&command::un_execute)); }
};
我们有了命令类,有个管理命令的类也许会更好,在使用时只需要从该类派生,那么你的类就有了undo/red代码如下:
// 命令中心
// 用于管理所有命令,实现撤消/重做,只需调用该类里的undo/redo。
class command_center
{
public:
command_center() : _recordcount(0), _maxcmd(100), _pause(false) {}
virtual ~command_center()
{
clear_undo_list();
clear_redo_list();
}
public:
virtual void push(command* cmd)
{
assert(cmd != NULL);
if(_recordcount > 0 && !_pause)
{
_microlist.push_back(cmd);
}
else
{
clear_redo_list();
_undolist.push_back(cmd);
do_push();
}
}
command* pop(void)
{
if(_undolist.empty())
return NULL;
command* pcmd = _undolist.back();
_undolist.pop_back();
return pcmd;
}
void undo()
{
if(_undolist.empty())
return;
command* cmd = _undolist.back();
cmd->un_execute();
_redolist.push_back(cmd);
_undolist.pop_back();
}
void redo()
{
if(_redolist.empty())
return;
command* cmd = _redolist.back();
cmd->execute();
_undolist.push_back(cmd);
_redolist.pop_back();
}
void do_cmd(command* cmd)
{
assert(cmd != NULL);
push(cmd);
cmd->execute();
}
bool can_undo() { return !_undolist.empty(); }
bool can_redo() { return !_redolist.empty(); }
// 注意: record和stop必须一起使用,先使用record标志宏命令开始,再使用stop标记宏命令结束,否则命令不能进入undo列表
// 2005-12-21修改过,将_recordcount由bool型改为了int型.这样record可以被调用多次.但stop调用次数必须与record的次数一致,否则第一个record之后的所有命令都不会加入到undo列表中._recordcount的值最小为0.
void record() { ++_recordcount; _pause = false; }
void stop() { _recordcount = (_recordcount > 0) ? --_recordcount : 0; composite(); _pause = false; }
void pause(bool pause_) { _pause = pause_; }
bool pause() { return _pause; }
bool recording() { return _recordcount != 0; }
protected:
virtual void do_push() {}
private:
// 将_macrolist中的所有命令组合成一个macro_cmd命令
void composite()
{
// _microlist为空或还没有到组合时机时不组合
if(_macrolist.empty() || _recordcount > 0)
return;
if(_microlist.size() == 1)
{
push(_macrolist.front());
}
else
{
macro_command* pMacroCmd = new micro_command;
pMacroCmd ->insert(pMacroCmd ->end(), _microlist.begin(), _microlist.end());
push(pMacroCmd );
}
_macrolist.clear();
}
void clear_undo_list()
{
cmd_deque::iterator ite = _undolist.begin();
for(; ite != _undolist.end(); ++ite)
{
delete *ite;
}
_undolist.clear();
}
void clear_redo_list()
{
cmd_deque::iterator ite = _redolist.begin();
for(; ite != _redolist.end(); ++ite)
{
if((*ite)->type() == type_create)
(*ite)->need_destory(true);
else if((*ite)->type() == type_delete)
(*ite)->need_destory(false);
else if((*ite)->type() == type_micro)
(*ite)->need_destory(true);
delete *ite;
}
_redolist.clear();
}
//
// 2006-09-30增加清空宏命令列表
void clear_macro_list()
{
cmd_deque::iterator ite = _macrolist.begin();
for(; ite != _macrolist.end(); ++ite)
{
if((*ite)->type() == type_create)
(*ite)->need_destory(true);
else if((*ite)->type() == type_delete)
(*ite)->need_destory(false);
else if((*ite)->type() == type_micro)
(*ite)->need_destory(true);
delete *ite;
}
_macrolist.clear();
}
protected:
typedef deque<command*> cmd_deque;
cmd_deque _undolist; // undo列表,用于保存需要撤消的所有命令
cmd_deque _redolist; // redo列表,用于保存需要重做的所有命令
cmd_deque _macrolist; // 组合命令列表,用于临时保存需要进行组合的命令
bool _pause; // 记录组合命令时,是否需要暂停记录.在调用record或stop时该状态自动取消.2006-11-29
int _recordcount; // 是否需要组合,当需要组合时,便将_microlist中列表命令组合成一个micro_cmd命令,再插入undo列表中.
int _maxcmd; // 最大命令列表数量
};