Redo Undo 的三种实现

原文地址

http://blog.163.com/min_zxm/blog/static/5846202920078171128560/

Redo Undo 的三种实现

Published on  2012 年 6 月 26 日, by  chenxin in  未分类.

在开发程序时,经常需要实现Redo Undo功能,这里我就谈谈三种Redo Undo的实现方式

实现Redo  Undo, 其核心思想是使用两个栈(UnDo栈,  ReDo栈)记录每一步操作,在撤销和重做时,弹出栈里的记录进行恢复。

这里,我们需要一个管理操作的类:

Source code    
class CommandManager
     {
         private Stack &lt ;ICommand &gt ; ReDoStack ;
         private Stack &lt ;ICommand &gt ; UnDoStack ;

         public CommandManager ( )
         {
            ReDoStack  =  new Stack &lt ;ICommand &gt ; ( ) ;
            UnDoStack  =  new Stack &lt ;ICommand &gt ; ( ) ;
         }
         public  void NewCommand (ICommand cmd )
         {
            UnDoStack. Push (cmd ) ;
            ReDoStack. Clear ( ) ;
         }
         public  void UnDo ( )
         {
            ICommand cmd  = UnDoStack. Pop ( ) ;
            ReDoStack. Push (cmd ) ;
            cmd. UnDo ( ) ;
         }
         public  void ReDo ( )
         {
            ICommand cmd  = ReDoStack. Pop ( ) ;
            UnDoStack. Push (cmd ) ;
            cmd. ReDo ( ) ;
         }
         public  void Clear ( )
         {
            ReDoStack. Clear ( ) ;
            UnDoStack. Clear ( ) ;
         }
     }

其使用NewCommand 记录一个操作Command
通过调用 UnDo方法,实现撤销
通过调用 ReDo方法,实现重做

ICommand接口如下:

Source code    
public  interface ICommand
     {
         void ReDo ( ) ;
         void UnDo ( ) ;
     }

 

 

 

操作如何记录,以下有三种方式:

 

一  单个对象记录操作
其思想是使用一个类实现记录各种操作类型,也就是说,这个类会包含所有类型操作所需要用到的属性,每次记录操作时,只使用其部分属性。

Source code    
public  class Command  : ICommand
     {
         object things ;   //需要操作的对象
        Type  type ;      //记录操作类型
         float x_move ;    //
         float y_move ;    //记录位移变化量
         float scale ;     //记录缩放变化量
         float rotate ;    //记录旋转变化量
        ...              //其他类型属性

         public Command (... )
         {
         }
         public  void ReDo ( )
         {
         }
         public  void UnDo ( )
         {
         }
     }

 

上面的示例中,
记录移动操作时,只使用x_move y_move属性
记录缩放操作时,只使用scale属性

执行Undo Redo时,根据记录的操作类型属性type, 选择性执行不同操作
(ReDo, UnDo里用switch实现)

使用单个对象表示变化的方法的优缺点
它的优点是实现简单,而不需要知道任何的设计模式,你就可以实现
Undo/Redo。
可维护性很低。代表该方法的对象包含很多额外信息,因为这里,单
个对象用来容纳所有动作类型的数据。例如,对移动而言,我们只需保存
移动相关的数据,而对调整尺寸,我们应该仅保存该操作相关的数据。所
以,我们在保存冗余的数据。随着操作数目的增加,冗余也在增加。这并
不是好的面向对象的设计。

 

二 命令模式实现

其思想就是对于不同操作,使用不同类进行记录,这些类都实现了同一个
接口ICommand。
每一个操作,都是一个命令,不同的操作类代表不同的命令。

Source code    
public  class MoveCommand  : ICommand
     {
         object things ;
         float x_move ;
         float y_move ;

         public MoveCommand (... )
         {
         }
         public  void ReDo ( )
         {
         }
         public  void UnDo ( )
         {
         }
     }

     public  class ScaleCommand  : ICommand
     {
         object things ;
         float scale ;

         public ScaleCommand (... )
         {
         }
         public  void ReDo ( )
         {
         }
         public  void UnDo ( )
         {
         }
     }

     public  class RotationCommand  : ICommand
     {
         object things ;
         float rotate ;

         public RotationCommand (... )
         {
         }
         public  void ReDo ( )
         {
         }
         public  void UnDo ( )
         {
         }
     }

     public  class AddCommand  : ICommand
     {
         object things ;
         object container ;

         public AddCommand (... )
         {
         }
         public  void ReDo ( )
         {
         }
         public  void UnDo ( )
         {
         }
     }

在上面每一个具体的操作类中,可以实现对应的方法。

使用命令模式的优缺点
它的可维护性很好而且没有任何冗余信息。它不是内存密集型,从我的观点来看,命令模式是实现多级Undo/Redo的最好方式。
命令模式的唯一缺点在于你不得不使命令的类型与操作的数目相等而无论一个操作是小或大。随着操作地增加,命令也在增加。

三 备忘录模式
其思想是完整保存需要ReDo UnDo区域的状态,我以在画布Canvas上做撤
销操作为例:

Source code    
public  class State
     { //保存画布Canvas的状态   既备忘录
         //保存画布里所有元素的深度拷贝
         public List &lt ;Element &gt ; ContainerState ;
         public State (List &lt ;Element &gt ; containerState )
         {
            _ContainerState  = containerState ;
         }
     }
     public  static  class Originator
     { //取得画布状态,恢复画布状态
         private  static Canvas Container ;    //程序中的画布引用
         public Originator (Canvas container )
         {
            Container  = container ;
         }
         public State getState ( )
         {
             //深度拷贝Container里的所有元素
         }
         public  void setState (State state )
         {
             //使用state里的状态重新恢复Container
         }

     }
     class CommandManager
     { //ReDo UnDo管理类
         private Stack &lt ;State &gt ; ReDoStack ;
         private Stack &lt ;State &gt ; UnDoStack ;

         public CommandManager ( )
         {
            ReDoStack  =  new Stack &lt ;State &gt ; ( ) ;
            UnDoStack  =  new Stack &lt ;State &gt ; ( ) ;
         }
         public  void NewCommand (State cmd )
         {
            UnDoStack. Push (cmd ) ;
            ReDoStack. Clear ( ) ;
         }
         public  void UnDo ( )
         {
            State cmd  = UnDoStack. Pop ( ) ;
            ReDoStack. Push (cmd ) ;
            Originator. setState (cmd ) ;
         }
         public  void ReDo ( )
         {
            State cmd  = ReDoStack. Pop ( ) ;
            UnDoStack. Push (cmd ) ;
            Originator. setState (cmd ) ;
         }
         public  void Clear ( )
         {
            ReDoStack. Clear ( ) ;
            UnDoStack. Clear ( ) ;
         }
     }

在每一次记录时,先使用State state = Originator.getState()获取当
前状态
然后使用CommandManager.NewCommand 添加状态
以后通过执行CommandManager.ReDo() UnDo() 进行撤销删除

备忘录模式优缺点

它的优点在于其变更管理的表现非常好。
由于在备忘录模式中,我们保存了容器的状态,因此它是内存密集型的。
这里你不得不对所有的对象和容器具有的属性进行深度拷贝。若你不能对
其中任一个进行深度拷贝时将出现问题。

 

 

几个需要考虑的问题:

内存问题:

若我们想支持无限级的Redo Undo操作,则只使用内存是不够的,所以我们需要在某些时候使用硬盘来保存记录。

如何抉择:

正常情况下,我们应该选择命令模式进行实现,其实现简单,操作方便,资源占用少,但是其不能应对所有情况(某些不可逆的操作:photoshop的某些不可逆的图形算法),这时,就应该使用备忘录模式,其完整保存之前状态,可以满足任何情况,唯一的不足是资源占用大。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值