博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从Undo,Redo谈命令模式
阅读量:7092 次
发布时间:2019-06-28

本文共 3394 字,大约阅读时间需要 11 分钟。

一般的应用软件中,通常会提供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上,待以后撤销使用:

有了以上数据结构,撤销和重做逻辑就很简单,当用户点击撤销的时候:

  1. 首先检查 commandUndoStack是否为空,如果为空,直接返回,否则继续.
  2. 从commandUndoStack中Pop出最近一次的操作对象ICommand对象
  3. 然后将该命令对象Push到commandRedoStack上保存以便以后重做。
  4. 最后Pop出来的命令对象ICommand的UndoExecute方法实现撤销.

和取消类似,当用户点击重做的时候

  1. 首先检查commandRedoStack是否为空,如果为空,直接返回,否则继续.
  2. 从commandRedoStack中Pop出最近一次的操作对象ICommand对象
  3. 然后将该命令对象Push到commandUndoStack上保存以便以后撤销。
  4. 最后Pop出来的命令对象ICommand的Execute方法实现重做.

整个CommandManager对象实现如下:

class CommandManager{    private Stack
commandUndoStack = new Stack
(); private Stack
commandRedoStack = new Stack
(); 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的功能简单演示了命令模式的实现及原理,希望对大家有所帮助。

转载地址:http://hsnql.baihongyu.com/

你可能感兴趣的文章
内存堆与栈的区别
查看>>
NHibernate初学者指南(12):日志
查看>>
30 个设计新颖的网站风格展示
查看>>
概念——统一资源定位符(Uniform / Universal Resource Locator,URL)
查看>>
Apache HttpComponents 获取Cookie
查看>>
彻底理解jdbc为什么用反射创建驱动程序对象
查看>>
Oracle内存管理(之五)
查看>>
[nio]dawn的基本概念
查看>>
【数据库摘要】6_Sql_Inner_Join
查看>>
交叉熵代价函数(损失函数)及其求导推导
查看>>
Android UI开源框架
查看>>
Java 构造时成员初始化的陷阱
查看>>
CentOS7.1 Liberty云平台之环境准备(2)
查看>>
js正则表达式test方法、exec方法与字符串search方法区别
查看>>
4.终端
查看>>
Vue webpack配置文件
查看>>
[k8s]docker calico网络&docker cluster-store
查看>>
kerberos认证原理---讲的非常细致,易懂
查看>>
cf1042F. Leaf Sets(贪心)
查看>>
优秀的 Spring Cloud 开源软件
查看>>