从Undo,Redo谈命令模式
一般的应用软件中,通常会提供Redo和Undo的操作,比如Paint.NET中的动作面板,Word中的撤销重做,一般我们按Ctrl-Z即可回退到上次操作。
要实现上面的这一功能,最直观的想法就是,我们需要把执行的命令以及相应的参数记录下来,一个命令或者动作,我们可以想象成一个对象,将这些的命令以对象的方式放到一个Stack里面,然后Undo的时候,Pop出来,然后执行该命令即可返回之前的状态。
将命令或者操作抽象为一个对象,使得可以用不同的请求参数对对象进行初始化,使得可以对命令进行排队处理,记录请求,以及执行Undo和Redo操作,这就是命令模式(Command Pattern),命令模式最大的优点就是,他将对象方法的调用和实现分离开。
为了说明如何实现Undo和Redo,我们尝试做一个简单的文本格式化的小工具,就是能够进行加粗,倾斜,加下划线,然后支持重做和撤销操作。
首先我们可以定义表示命令执行的抽象类或者接口,这里使用借口ICommand,里面有Execute和UndoExecute两个方法:
public interface ICommand { void Execute(); void UndoExecute(); }
然后我们的所有命令都实现这一接口:
class BoldCommand : ICommand { private Document document; private string previousText; public BoldCommand(Document document) { this.document = document; previousText = this.document.Text; } public void Execute() { document.BoldSelection(); } public void UndoExecute() { document.Text = previousText; } }
这个是加粗的命令的接口,参数Document表示编辑的页面,通过构造函数传进来,并且保留处理之前的文本样式。在执行方法Execute中,执行document的BoldSelection方法,在UndoExecute方法中,直接将之前未处理的文字复制给当前的文字状态予以显示。
其他的方法UnderlineCommand,ItalicizeCommand方法和这个类似,这里就不一一显示。
然后,我们需要建立一个管理这些命令的管理类。在这个类中我们两个Stack,commandUndoStack用来来保存所有的可以取消的操作,commandRedoStack用来保存所有重做的操作。方法执行的时候,首先调用ICommand方法的Execute方法,然后将该命令对象Push到UndoStack上,待以后撤销使用:
有了以上数据结构,撤销和重做逻辑就很简单,当用户点击撤销的时候:
- 首先检查 commandUndoStack是否为空,如果为空,直接返回,否则继续.
- 从commandUndoStack中Pop出最近一次的操作对象ICommand对象
- 然后将该命令对象Push到commandRedoStack上保存以便以后重做。
- 最后Pop出来的命令对象ICommand的UndoExecute方法实现撤销.
和取消类似,当用户点击重做的时候
- 首先检查commandRedoStack是否为空,如果为空,直接返回,否则继续.
- 从commandRedoStack中Pop出最近一次的操作对象ICommand对象
- 然后将该命令对象Push到commandUndoStack上保存以便以后撤销。
- 最后Pop出来的命令对象ICommand的Execute方法实现重做.
整个CommandManager对象实现如下:
class CommandManager { private Stack<ICommand> commandUndoStack = new Stack<ICommand>(); private Stack<ICommand> commandRedoStack = new Stack<ICommand>(); public void ExecuteCommand(ICommand cmd) { cmd.Execute(); commandUndoStack.Push(cmd); } public void Undo() { if (commandUndoStack.Count > 0) { ICommand cmd = commandUndoStack.Pop(); cmd.UndoExecute(); commandRedoStack.Push(cmd); } } public void Redo() { if (commandRedoStack.Count > 0) { ICommand cmd = commandRedoStack.Pop(); cmd.Execute(); commandUndoStack.Push(cmd); } } }
最后,当用户执行某一命令的时候,只需要实例化一个CommandManager管理类,然后将待执行的命令ICommand传进去作为参数执行即可。
private CommandManager commandManager; private Document document; public Form1() { InitializeComponent(); document = new Document(this.textBox1); commandManager = new CommandManager(); } private void btnBold_Click(object sender, EventArgs e) { ICommand boldCommand = new BoldCommand(document); commandManager.ExecuteCommand(boldCommand); } private void btnItalic_Click(object sender, EventArgs e) { ICommand italicCommand = new ItalicizeCommand(document); commandManager.ExecuteCommand(italicCommand); } private void btnUnderLine_Click(object sender, EventArgs e) { ICommand underLineCommand = new UnderlineCommand(document); commandManager.ExecuteCommand(underLineCommand); } private void btnRedo_Click(object sender, EventArgs e) { commandManager.Redo(); } private void btnUndo_Click(object sender, EventArgs e) { commandManager.Undo(); }
整个代码的UML结构图如下,代码简洁清晰吧。
本文通过实现一个简单的Undo,Redo的功能简单演示了命令模式的实现及原理,希望对大家有所帮助。
从Undo,Redo谈命令模式的更多相关文章
- 深入浅出设计模式——命令模式(Command Pattern)
模式动机 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计,使得请 ...
- 设计模式(十四):Command命令模式 -- 行为型模式
1.概述 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来 ...
- 设计模式 ( 十三 ) 命令模式Command(对象行为型)
设计模式 ( 十三 ) 命令模式Command(对象行为型) 1.概述 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需 ...
- Head First 设计模式之命令模式(CommandPattern)
前言: 本章会将封装带入到一个全新的境界,把方法调用封装起来.通过封装方法调用,把运算块包装成形.调用此运算的对象不需要知道事情是如何进行的,只要知道如何使用包装形成的方法来完成它就ok了. 1 现实 ...
- HeadFirst设计模式之命令模式
一. 1.因为是操作经常变化,所以封装操作为command对象.You can do that by introducing “command objects” into your design. A ...
- 命令模式(head first 设计模式5)
一.命令模式定义 命令大家都不会陌生,那么在开始命令模式之前,可以想象一下生活中的命令模式的特点: 如老板命令你完成一个OA项目是一个命令,接着看看其特点: 1.在上面的命令中,命令的执行者肯定是聪明 ...
- C#设计模式之11:命令模式
C#设计模式之11:命令模式 命令模式 命令模式用来解决一些复杂业务逻辑的时候会很有用,比如,你的一个方法中到处充斥着if else 这种结构的时候,用命令模式来解决这种问题就会让事情变得简单很多. ...
- 命令模式-实现undo和redo
这次实验主要是实现多次redo和undo,即程序的撤回和恢复,这里只实现加法的撤回和恢复. 程序的撤回和恢复就是由所使用的软件来记录操作步骤,可以将数据恢复到某个操作状态. 撤回这个指令很常见,Win ...
- 设计模式 - 命令模式(command pattern) 撤销(undo) 具体解释
命令模式(command pattern) 撤销(undo) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy 參考命令模式: http://blog.cs ...
随机推荐
- AutoMapper实现自动CreapMap
标题是个噱头,完全不写代码自动是不现实的,只是简化了CreateMap.方法也是很粗糙的,看看吧. 我想在使用AutoMapper的时候最恶心的一定是写了一个Profile,里边有n行 Mapper. ...
- CozyRSS开发记录20-CanResizeWithGrip
CozyRSS开发记录20-CanResizeWithGrip 1.窗口样式 首先,WindowStyle有四种: 然后,对于窗口缩放的ResizeMode,也有四种,CanResize和CanRes ...
- Project Euler欧拉计划
1 If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. Th ...
- Flex Layout Attribute
GitHub: https://github.com/StefanKovac/flex-layout-attribute 引入基本的样式,可以更好的布局,可以在线制作: http://progress ...
- linux进程
E: 进程------->进程控制块PCB 结构体 进程控制块中,存放的是指针数组------>是已经打开的文件的结构体的指针 文件描述符实际上就是指针数组的索引 e1: ps命令(进 ...
- js判断当前页面在移动设备还是在PC端中打开
方法一: var isPC = function () { var userAgentInfo = navigator.userAgent.toLowerCase(); var Agents = ne ...
- Puppet自动化部署-安装及配置(3)
本文介绍Puppet Master及Agent相关的安装及配置. 一. 官网下载Puppet安装YUM源 [root@puppet-master ~]# rpm -ivh https://yum.pu ...
- 转:学习笔记:delphi多线程学识
学习笔记:delphi多线程知识 最近一直在温习旧的知识,刚好学习了一下Java的线程安全方面的知识,今天想起之前一直做的Delphi开发,所以还是有必要温习一下,看看这些不同的编程语言有什么不同之处 ...
- Thisgood
this is good test print 'haha'
- PS特效精粹
冲击有力的广告效果 神奇画笔功能 + 强大的图层样式 Photoshop中的三维特效