C#设计模式之19——备忘录模式

备忘录模式用来保存与对象有关的数据,这样可以在将来对对象进行复原。例如在绘图程序中需要保存对象的颜色尺寸等。

备忘录模式中定义了三个角色:

1. 发起者:指我们要保存其状态的对象,比如说一个图形对象的位置,颜色等。

2. 备忘录: 保存发起者状态的对象。

3. 监护者: 管理状态的保存时机,保存备忘录,以及如果需要的话使用备忘录回复发起者的状态。

 

我们举个例子来具体的了解备忘录模式。假设我们有一个程序,在面板中可以绘制矩形,或者选中一个矩形可以移动位置,或者可以选择undo,取消上一步操作。

在Undo 中,就需要通过备忘录来记录以前矩形的状态。我们这里的例子不仅使用了备忘录模式,还是用了命令模式,因为命令模式可以使得所有按钮都统一的操作;而且还使用了中介者模式,各个按钮的状态通过中介者来进行通信。

下面是实现的各个按钮的代码,每个按钮都实现Command接口:

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;

namespace Memento
{
	/// <summary>
	/// Summary description for ClrButton.
	/// </summary>
	public class ClrButton : System.Windows.Forms.Button , Command
	{
		/// <summary> 
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;
		private Mediator med;
		//-----
		public ClrButton(Mediator md) 		{
			InitializeComponent();
			med = md;
		}
		//-----
		public void Execute() {
			med.clear ();
		}

		/// <summary> 
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		#region Component Designer generated code
		/// <summary> 
		/// Required method for Designer support - do not modify 
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			components = new System.ComponentModel.Container();
		}
		#endregion
	}
}

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;

namespace Memento
{
	/// <summary>
	/// Summary description for UndoButton.
	/// </summary>
	public class UndoButton :  Command
	{
		private ToolBarButton ubutton;
		private Mediator med;

		public UndoButton(Mediator md, ToolBarButton but) 	{
			med = md;
			ubutton = but;
		}
		public void Execute() {
			med.undo ();
		}
		

	}
}

using System.Windows.Forms;namespace Memento{/// <summary>/// Summary description for RectButton./// </summary>public class RectButton : Command {private ToolBarButton bt;private Mediator med;//------public RectButton(Mediator md, ToolBarButton tb) {med = md;bt = tb;}//------public void setSelected(bool sel) {bt.Pushed = sel;}//------public void Execute() {if(bt.Pushed )med.startRectangle ();}}}


通过代码可以看出,所有按钮的代码都是通过中介者来执行的。

 

我们创建一个监护者类来管理撤销行为的列表,中介者维护绘制对象列表,以及与监护者类通信。

矩形类的定义:

using System;

namespace CsharpPats
{
	/// <summary>
	/// Summary description for Rectangle.
	/// </summary>
	public class Rectangle
	{
		private int xp, yp, wr, hr;
		public Rectangle(int x, int y, int w, int h) {
			xp = x;
			yp = y;
			wr = w;
			hr = h;
		}
		public Rectangle(float x, float y, float w, float h) {
			xp = (int)x;
			yp = (int)y;
			wr = (int)w;
			hr = (int)h;
		}
		public bool contains(int x, int y) {
			bool cn = xp <= x && x <= xp + wr;
            cn = cn && yp <= y && y <= yp + hr;
            return cn;
		}
		public int x {
			get{
				return xp;
			}
			set {
				xp = value;
			}
		}
		public int y {
			get{
				return yp;
			}
			set {
				yp = value;
			}
		}
		public int w {
			get{
				return wr;
			}
			set {
				wr = value;
			}
		}
		public int h {
			get{
				return hr;
			}
			set {
				hr = value;
			}
		}
	}
}


矩形类的监护者类:

using System;
using System.Drawing ;
using CsharpPats;
namespace Memento
{
	/// <summary>
	/// Summary description for VisRectangle.
	/// </summary>
	public class VisRectangle 	{
		private int x, y, w, h;
		private const int VSIZE=30;
		private const int HSIZE=50;
		private CsharpPats.Rectangle rect;
		private bool selected;
		private Pen bPen;
		private SolidBrush bBrush;
		//-----
		public VisRectangle(int xp, int yp) 		{
			x = xp; 			y = yp;
			w = HSIZE;			h = VSIZE;
			saveAsRect();
			bPen = new Pen(Color.Black);
			bBrush = new SolidBrush(Color.Black);
		}
		//-----
		//used by Memento for saving and restoring state
		internal CsharpPats.Rectangle rects {
			get {
				return rect;
			}
			set {
				x=value.x;
				y=value.y;
				w=value.w;
				h=value.h;
				saveAsRect();
			}
		}
		//------
		public void setSelected(bool b) {
			selected = b;
		}
		//-----
		//move to new position
		public void move(int xp, int yp) {
			x = xp; 
			y = yp;
			saveAsRect();		
		}
		//-----
		public void draw(Graphics g) { 
			//draw rectangle
			g.DrawRectangle(bPen, x, y, w, h);
			
			if (selected) {   //draw handles
				g.FillRectangle(bBrush, x + w / 2, y - 2, 4, 4);
				g.FillRectangle(bBrush, x - 2, y + h / 2, 4, 4);
				g.FillRectangle(bBrush, x + (w / 2), y + h - 2, 4, 4);
				g.FillRectangle(bBrush, x + (w - 2), y + (h / 2), 4, 4);
			}
		}
		//-----
		//return whether point is inside rectangle
		public bool contains(int x, int y) {
			return rect.contains (x, y);
		}
		//------
		//create Rectangle object from new position
		private void saveAsRect() {
			rect = new CsharpPats.Rectangle (x,y,w,h);
		}
	}
}


然后就是我们的备忘录的设计,备忘录类要保存矩形对象的位置信息:

using System;

namespace Memento
{
	/// <summary>
	/// Summary description for Memento.
	/// </summary>
	public class Memento 	{
		private int x, y, w, h;
		private CsharpPats.Rectangle rect;
		private VisRectangle visRect;
		//------
    	public Memento(VisRectangle vrect) 		{
			visRect = vrect;
			rect = visRect.rects ;
			x = rect.x ;
			y = rect.y;
			w = rect.w;
			h = rect.h;
		}
		//------
		public void restore() {
			rect.x = x;
			rect.y = y;
			rect.h = h;
			rect.w = w;
			visRect.rects = rect;
		}
	}
}


 

在创建Memento类的实例的时候,我们使用初始化方法把想要保存的VisRectangle类传递给它,Memento类复制尺寸和位置参数,并保存一个VisRectangle自身的一个实例。然后,当我们需要还原这些参数的时候,Memento 类的实例就会知道必须把这些参数还原给哪一个实例,并且可以直接这样做。

接下来是中介者类的设计:

using System;
using System.Windows.Forms;
using System.Collections ;
using System.Drawing;
namespace Memento
{
	/// <summary>
	/// Mediates events between buttonsb
	/// </summary>
	public class Mediator
	{
		private bool startRect;
		private bool rectSelected;
		private ArrayList drawings;
		private PictureBox canvas;
		private int selectedIndex;
		private CareTaker caretakr;
		private RectButton rect;
		private VisRectangle v;
		private VisRectangle[] draw_ings;	//used only to make clearer UML diagram
		//-----------------
		public Mediator(PictureBox p) 		{
			startRect = false;
			rectSelected = false;
        
			drawings = new ArrayList();
			caretakr = new CareTaker(drawings);
			canvas = p;
		}
		//------
		public void startRectangle() {
			startRect = true;
		}
		//-----
		public void createRect(int x, int y) { 
			unpick();         //make sure no rectangle is selected
			if (startRect) {  //if rect button is depressed
				int count = drawings.Count;
				caretakr.Add(count);  //Save previous drawing list size
				v = new VisRectangle(x, y);    //create a rectangle
				drawings.Add(v);            //add new element to list
				startRect = false;          //done with this rectangle
				rect.setSelected(false);    //unclick button
				canvas.Refresh();
			}
			else
				pickRect(x, y);   //if not pressed look for rect to select
		}
		//-----
		public void registerRectButton(RectButton rb) {
			rect = rb;
		}
		//-----
		public void unpick() {
			if (rectSelected && (selectedIndex >= 0) && (selectedIndex < drawings.Count)) {
				VisRectangle vis = (VisRectangle) drawings[selectedIndex];
				vis.setSelected(false);
				selectedIndex = -1;
				rectSelected = false;
				canvas.Refresh();
			}
		}
		//-----
		public void pickRect(int x, int y) {
			//save current selected rectangle
			//to avoid double save of undo
			int lastPick = -1;     
			if (selectedIndex >= 0) {
				lastPick = selectedIndex;
			}
			unpick();  //undo any selection
			//see if one is being selected
			for (int i = 0; i< drawings.Count; i++) {
				v = (VisRectangle)drawings[i];
				if (v.contains(x, y)) { //did click inside a rectangle
					selectedIndex = i;     //save it
					rectSelected = true;
					if (selectedIndex != lastPick) { //but don't save twice
						caretakr.rememberPosition(v);
					}
					v.setSelected(true);    //turn on handles
					repaint();          //and redraw
				}			
			}
		}
		//-----
		public void clear() {
			 drawings = new ArrayList();
			caretakr.clear(drawings);
			rectSelected = false;
			selectedIndex = 0;
			repaint();
		}
	//-----
		private void repaint() {
			canvas.Refresh ();
		}
		//-----
		public void undo() {
			caretakr.undo ();
			repaint();
		}
		//-----
		public void reDraw(Graphics g) {
			for(int i=0; i < drawings.Count ; i++ ) {
				VisRectangle v = (VisRectangle)drawings[i];
				v.draw (g);
			}
		}
		//-----
		public void drag(int x, int y) {
			if (rectSelected) {
				VisRectangle v = (VisRectangle)drawings[selectedIndex];
				if(v.contains (x, y)) {
					v.move (x, y);
					repaint();
				}
			}
		}
	}
}


然后是监护者类:

using System;
using System.Collections ;
namespace Memento
{
	/// <summary>
	/// Summary description for CareTaker.
	/// </summary>
	public class CareTaker
	{
		private ArrayList drawings, undoList;
		private Memento mem;
		private VisRectangle[] draw_ings; //used only to make UML clearer
		public CareTaker(ArrayList dcol)
		{
			clear(dcol);
		}
		public void rememberPosition(VisRectangle vr) {
			mem = new Memento (vr);
			undoList.Add (mem);
		}
		public void clear(ArrayList drw) {
			drawings = drw;
			undoList = new ArrayList();
		}
		public void Add(int intg) {
			undoList.Add (intg);
		}
		public void removeDrawing() {
			drawings.RemoveAt (drawings.Count -1);
		}
		public void remove(Memento mem) {
			mem.restore ();
		}
		public void remove(int intg) {
			removeDrawing();
		}
		public void undo() {
			if(undoList.Count > 0) {
				int last = undoList.Count -1;
				object obj = undoList[last];
				try{
					Memento mem = (Memento)obj;
					remove(mem);
				}
				catch (Exception) {
					removeDrawing();
				}
				undoList.RemoveAt (last);
			}
		}
	}
}


 

监护者类维护了一个撤销行为的列表,该列表是整数和Memento对象的一个集合,如果值是一个整数,就代表了这一个时候要绘制的对象的个数;如果值是一个Memento对象,就代表了某个要还原的VisRectangle类实例之前的状态。我们的undo方法只是决定,是绘制列表减少一个绘制对象呢,还是回复一个绘制对象的位置。

 

然后就是主程序的代码构建了:

private System.Windows.Forms.PictureBox pic;
		private System.Windows.Forms.ToolBar tbar;
		private System.Windows.Forms.ToolBarButton btRect;
		private System.Windows.Forms.ToolBarButton btUndo;
		private System.Windows.Forms.ToolBarButton btClear;
		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;
		private bool mouse_down;
		private Mediator med;
		private Hashtable commands;
		//-----
		public Form1() 		{
			InitializeComponent();
			init();						
		}
		//-----
		private void init() {
			med = new Mediator(pic);     //create Mediator
			commands = new Hashtable();  //and Hash table
			//create the command objectsb
			RectButton rbutn = new RectButton(med, tbar.Buttons[0]);
			UndoButton ubutn = new UndoButton(med, tbar.Buttons[1]);
			ClrButton clrbutn = new ClrButton(med);
			med.registerRectButton (rbutn);
			//add them to the hashtable using the button hash values
			commands.Add(btRect.GetHashCode(), rbutn);
			commands.Add(btUndo.GetHashCode(), ubutn);
			commands.Add(btClear.GetHashCode(), clrbutn);
			pic.Paint += new PaintEventHandler (paintHandler);
		}
		private void paintHandler(object sender, PaintEventArgs e ) {
			Graphics g =  e.Graphics ;
			med.reDraw (g);
		}		


 

一些鼠标事件:

private void tbar_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e) {
			ToolBarButton tbutn = e.Button ;
			Command comd = (Command)commands[tbutn.GetHashCode ()];
			comd.Execute ();
		}
		//------
		private void pic_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) {
			mouse_down = true;
			med.createRect (e.X, e.Y);
		}
		//------
		private void pic_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) {
			mouse_down = false;
		}
		//------
		private void pic_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) {
			if(mouse_down)
				med.drag(e.X , e.Y);
		}


 

在可做到的编程语言中,备忘录模式提供了一种在维持对象状态的同时维持封装性的方法。应该只有发起者类对其他有访问权限的数据就有效的保持了其私有性。该模式还通过把信息的保存和还原委托给某个备忘录类而维持了发起者的简单性。但是备忘录模式有时候也可能因为要保存的信息量非常大,会占用相当多的存储空间,影响系统的运行效率。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值