设计模式在二维图形程序里的应用(命令模式--command)

说明:     本文只主要讲述一些设计模式在二维图形程序里的应用.不涉及到二维图形程序的算法.

最近很忙,没时间写。希望看到的朋友别介意,有时间我会好好整理一下的,希望对正在做这方面的朋友有用。

本文讲述命令模式的一个实现,此实现是笔者项目中实际用到的.
按照设计模式的命令模式,我们先定义一个基类,如下:
// 命令类型
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;  // 最大命令列表数量
};

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值