C#中通过Command模式实现Redo/Undo方案
作者:天方 发布时间:2021-07-12 14:58:59
标签:C#,Command,模式,Redo,Undo
一个比较常见的改进用户体验的方案是用Redo/Undo来取代确认对话框,由于这个功能比较常用,本文简单的给了一个在C#中通过Command模式实现Redo/Undo方案的例子,以供后续查询。
class Program
{
static void Main(string[] args)
{
var cmds = new CommandManager();
while (true)
{
var key = Console.ReadKey(true);
if (key.KeyChar >= '0' && key.KeyChar <= '9')
{
cmds.DoNewCommand(key.KeyChar.ToString(), () => Console.WriteLine("process " + key.KeyChar), () => Console.WriteLine("redo " + key.KeyChar));
}
else
{
if (key.Modifiers.HasFlag(ConsoleModifiers.Control) && (key.Key == ConsoleKey.Z))
cmds.UnDo();
else if (key.Modifiers.HasFlag(ConsoleModifiers.Control) && (key.Key == ConsoleKey.Y))
cmds.ReDo();
}
}
}
}
class CommandManager
{
#region Command定义
public class Command
{
string name;
Action action;
Action unDoAction;
internal Command(string name, Action action, Action unDoAction)
{
this.name = name;
this.action = action;
this.unDoAction = unDoAction;
}
internal void Do() { action(); }
internal void UnDo() { unDoAction(); }
public override string ToString()
{
return name.ToString();
}
}
#endregion
public Stack<Command> ReDoActionStack { get; private set; }
public Stack<Command> UnDoActionStack { get; private set; }
public CommandManager()
{
ReDoActionStack = new Stack<Command>();
UnDoActionStack = new Stack<Command>();
}
public void DoNewCommand(string name, Action action, Action unDoAction)
{
var cmd = new Command(name, action, unDoAction);
UnDoActionStack.Push(cmd);
ReDoActionStack.Clear();
cmd.Do();
}
public void UnDo()
{
if (!CanUnDo)
return;
var cmd = UnDoActionStack.Pop();
ReDoActionStack.Push(cmd);
cmd.UnDo();
}
public void ReDo()
{
if (!CanReDo)
return;
var cmd = ReDoActionStack.Pop();
UnDoActionStack.Push(cmd);
cmd.Do();
}
public bool CanUnDo { get { return UnDoActionStack.Count != 0; } }
public bool CanReDo { get { return ReDoActionStack.Count != 0; } }
//public IEnumerable<Command> Actions { get { return ReDoActionStack.Reverse().Concat(UnDoActionStack); } }
}
原理很简单,通过Command模式把每一步操作封装成一个可undo的命令(包含do和redo两个操作)。并将每一步操作执行后用栈保存起来,undo的时候就以此将Command依次出栈,并执行undo操作。(从某种意义上来说,redo就是undo操作的undo)
上面的代码已经实现了基本的Undo/Redo功能,但实际使用的时候还是有一些细节需要考虑的:如undo或redo时失败(抛异常)的处理等。由于这些细节方面的处理方式不尽相同,本文只是实现一个基本框架,以备后续使用时参考,并不想把它弄的过于复杂。
这种方式比较简单,几乎每种语言都可以轻易的写出这种方式下的实现。但通过这种Command封装的方式实现的也有一些限制,使用的时候需要注意:
每一步操作都需要封装成command命令
每一步操作都是可逆的
当命令过多的时候需要考虑commandlist的内存占用和命令查询时的性能问题
来源:https://www.cnblogs.com/TianFang/archive/2010/10/05/1844385.html


猜你喜欢
- 对于大规模乱序的数组,插入排序很慢,因为它只会交换相邻的元素,因此元素只能一点一点地从数组地一段移动到另一端。希尔排序改进了插入排序,交换不
- 目录一、Actuator简介二、与SpringBoot2.0整合 1、核心依赖Jar包2、Yml配置文件三、监控接口详解 
- JAVA并发编程有界缓存的实现1、有界缓存的基类package cn.xf.cp.ch14;/** * *功能:有界缓存实现基类 *时间:
- 写在前面在这里,我们将会学习怎么利用java8 快速的打印出需要打印的元素利用stream打印元素在Java中,有三种不同的方法来打印Jav
- 简介备忘录设计模式(Memento Design Pattern)也叫作快照(Snapshot)模式,主要用于实现防丢失、撤销、恢复等功能。
- 我就废话不多说了,大家还是直接看代码吧~using UnityEngine;using System.Collections; public
- 目录1、synchronized锁的底层实现原理2、基于synchronized实现单例模式3、利用类加载实现单例模式(饿汉模式)1、syn
- 1、准备使用redis实现分布式锁,需要用的setnx(),所以需要集成Jedis需要引入jar,jar最好和redis的jar版本对应上,
- 一、策略模式到底是什么?策略模式属于对象的行为模式。其用意是针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替
- 开始 在本文中,我将展示如何使用各种不同的 Java 技术构建一些简单的 Comet 风格的 Web 应
- 手动将本地jar添加到Maven仓库将jar添加到本地仓库的做法以下面pom.xml依赖的jar包为例:实际项目中pom.xml依赖写法:&
- Android 和 H5 都是移动开发应用的非常广泛。市面上很多App都是使用Android开发的,但使用Android来开发一些比较复杂附
- 这篇文章主要介绍了spring boot多数据源动态切换代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值
- 要获得打印机的状态,应该定义一个联合.enum PrinterStatus { 其他状态= 1, 未知, 空闲
- 前言本篇文章主要介绍的是SpringBoot项目进行全局异常的处理。SpringBoot全局异常准备说明:如果想直接获取工程那么可以直接跳到
- 设立一个定时器tmrMonitor,该定时器会在程序运行时不断把程序的占用内存和占用线程数写到LOG\MEM目录下。我设置的定时器间隔是30
- 前言在第一篇文字中,我们完全人工方式,一个命令一个命令输入,实现一个java tomcat运行环境,虽然也初见成效,但很累人。如果依靠依靠脚
- 前言服务消费者调用服务提供者的时候使用RestTemplate技术存在不便之处:拼接urlrestTmplate.getForObJect这
- 之前不怎么了解这个,一直以为做起来很复杂。 直到前两天公司要求要做这个功能。 做了之后才发现 这不过就是一个POST请求就能实现的东西。现在
- 首先来说一下本文中例子所要实现的功能:基于ProtoBuf序列化对象使用Socket实现时时通信数据包的编码和解码下面来看具体的步骤:一、U