使用Command模式实现应用的撤销功能

使用Command模式实现撤销机制[1]

 

Written by Matt Berther

Translated by Allen Lee[2]

Reviewed by Teddy Tam & Allen Lee

Introduction

Command是一个非常强大的设计模式,它的作用是将一个请求封装成一个对象,从而使你能够把来自客户端的不同请求(request)、队列(queue)或者日志记录请求(log request)包装成参数,并且还支持可撤销操作。

这个模式的一个最大的优点就是,它能够把执行某操作的对象和实际知道如何处理该操作的对象之间的耦合度降低。

今天,我要向大家介绍如何使用这个Command模式来实现撤销功能。至于我们的例子,我们将会开发一个非常简单的类记事本(Notepad clone)。你无须大惊小怪,因为这(个记事本)已经能够展现出这个模式的威力了。

The Code

我们要做的第一件事就是创建一个用于包装TextBox控件的抽象层(abstraction)。在Command模式里,这个抽象层被称为接收者(Reciever)。在我们的例子里面,这个接收者是一个叫做Document的对象。

 

class Document
{
	private TextBox textbox;

	public Document(TextBox textbox)
	{
	this.textbox = textbox;
	}

	public void BoldSelection()
	{
		Text = String.Format("<B>{0}</B>", Text);
	}

	public void UnderlineSelection()
	{
		Text = String.Format("<u>{0}</u>", Text);
	}

	public void ItalicizeSelection()
	{
		Text = String.Format("<I>{0}</I>", Text);
	}

	public void Cut()
	{
		textbox.Cut();
	}

	public void Copy()
	{
		textbox.Copy();
	}

	public void Paste()
	{
		textbox.Paste();
	}

	public string Text
	{
		get 
		{ 
			return textbox.Text; 
		}
		set 
		{
			textbox.Text = value;
		}
	}
}

我们要为Document对象定义的是那些能脱离当前文档而执行的操作,并把该功能从我们的主程序中完全解耦。将当前选定的文本变为黑体后,若要改变这个设置,我们应回到这个对象而非界面代码。

接着,我们需要设计一下Command的接口[3]。由于某些命令不要求撤销功能(例如“复制”),于是我们需要创建两个基类(Command和UndoableCommand)。稍后我们将会看到如何整合UndoableCommand。而现在,你只需记住这是一个用于被其它需要具备撤销功能的命令继承的类就行了。

public abstract class Command
{
	public abstract void Execute();
}

public abstract class UndoableCommand : Command
{
	public abstract void Undo();
}


随着我们完成我们的应用程序并向其中加入菜单项,我们将会看到我们需要一个Command对象来处理每这些菜单操作。那么,要处理黑体化,让我们先写下如下代码:

class BoldCommand : UndoableCommand
{
	private Document document;
	
	private string previousText;
	
	public BoldCommand(Document doc)
	{
		this.document = doc;
		previousText = this.document.Text;
	}
	
	public override void Execute()
	{
		document.BoldSelection();
	}
	
	public override void Undo()
	{
		document.Text = previousText;
	}
}


通过创建一个文档对象来包装TextBox,我们可以降低将要执行某操作的对象(菜单项)和实际知道如何处理该操作的对象(文档对象)之间的耦合度。

剩余命令对象与上述非常相似,因此我就不一一介绍所有的代码了,而且这些代码可以通过下载获得。

接下来我们需要一个将所有的这些命令整合到一起的CommandManager。CommandManager是一个非常简单的类,它的内部只有一个堆栈(stack),该堆栈用于保存并跟踪我们那些具备撤销功能的命令。

class CommandManager
{
	private Stack commandStack = new Stack();
	
	public void ExecuteCommand(Command cmd)
	{
		cmd.Execute();
		if (cmd is UndoableCommand)
		{
			commandStack.Push(cmd);
		}
	}
		
	public void Undo()
	{
		if (commandStack.Count > 0)
		{
			UndoableCommand cmd = (UndoableCommand)commandStack.Pop();
			cmd.Undo();
		}
	}
}


从上面的代码我们可以看到,我们仅仅把那些是UndoableCommand的命令加入到撤销堆栈。还记得我曾经说过我们将看到如何整合它(UndoableCommand)吗?这就是了。我们不希望不具备撤销功能的命令备加入到该队战中。若用户尝试撤消本身不支持撤消功能的某物,将得不到回应。

余下的工作就是替换菜单项的事件处理程序(有需要也可替换工具拦的)。

public class MainForm : System.Windows.Forms.Form
{
	private System.Windows.Forms.TextBox documentTextbox;
	
	private CommandManager commandManager = new CommandManager();
	
	private Document document;
	
	public MainForm()
	{
		//
		// Required for Windows Form Designer support
		//
		InitializeComponent();
	
		document = new Document(this.documentTextbox);
	}
	
	// a bunch of snipped code
	
	private void cutMenuItem_Click(object sender, System.EventArgs e)
	{
		commandManager.ExecuteCommand(new CutCommand(document));
	}
	
	private void pasteMenuItem_Click(object sender, System.EventArgs e)
	{
		commandManager.ExecuteCommand(new PasteCommand(document));
	}
	
	// etc
}


我写了一个完整的示范程序并提供了下载。请注意,在这里,这个文本编辑器是尚未完善的初级品。而你的任务,如果你愿意接受的话,为这个程序加入重做(redo)的功能[4]

希望我已阐明此模式之威力及如何轻松使用其来添加合成功能。现在添加新的命令非常容易了,因为你不再需要触及任何现有的代码。

 

Copy fromhttp://blog.163.com/wapuzxl@126/blog/static/137249957200642332256171/

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值