您现在的位置: 中国男护士网 >> 考试频道 >> 计算机等级 >> 二级辅导 >> C语言 >> 辅导 >> 正文    
  C#基础Memento备忘录模式(行为型模式) 【注册男护士专用博客】          

C#基础Memento备忘录模式(行为型模式)

www.nanhushi.com     佚名   不详 

  在软件构建过程中,某些对象的状态在转换过程中,可能由于某种需要,要求程序能够回溯到对象之前处于某个点时的状态.如果使用一些共有接口来让其他对象得到对象的状态,便会暴露对象的细节实现。我们需要实现对象状态的良好保存与恢复,但同时不会因此而破坏对象本身的封装性。
  Examda提示:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。
  我们首先看看不适用设计模式来解决对象状态恢复的情况。
  public class Rectangle : ICloneable
  {
  int x;
  int y;
  int width;
  int height;
  public void SetValue(Rectangle r)
  {
  this.x = r.x;
  this.y = r.y;
  this.width = r.width;
  this.height = r.height;
  }
  public Rectangle(int x, int y, int width, int height)
  {
  this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;
  }
  public void MoveTo(Point p)
  {
  //....
  }
  public void ChangeWidth(int width)
  {
  }
  public void ChangeHeight(int height)
  {
  }
  public void Draw(Graphics graphic)
  {
  }
  #region ICloneable 成员
  public object Clone()
  {
  return this.MemberwiseClone();
  }
  #endregion
  }
  public class GraphicsSystem
  {
  //原发器对象:
  //有必要对自身内部状态进行保存,然后在某个点处又需要恢复内部状态的对象
  Rectangle r = new Rectangle(0, 0, 10, 10);
  //备忘录对象:
  //保存原发器对象的内部状态,但不提供原发器对象支持的操作
  Rectangle rSaved = new Rectangle(0, 0, 10, 10);
  public void Process()
  {
  rSaved = r.Clone();
  //....
  }
  public void Saved_Click(object sender, EventArgs e)
  {
  r.SetValue(rSaved);
  //....
  }
  }
  class Program
  {
  static void Main(string[] args)
  {
  Rectangle r = new Rectangle(0, 0, 10, 10);
  GraphicsSystem g = new GraphicsSystem();
  g.Process(r);
  }
  }
  上面的代码中Rectangle类实现了ICloneable接口,这个接口利用浅拷贝返回一个新的Rectangle对象。


  在GraphicsSystem类中,我们定义了一个原发器对象r,和备忘录对象rSaved,在Process的时候,我们将原发器对象进行克隆保存在rSaved引用中。在Saved_Click方法中,将备忘录对象rSaved保存的值还原给原发器对象r。但这样来做,备忘录对象提过了原发器对象的一些操作,那么我们现在需要将备忘录对象抽象出来。
  public class Rectangle
  {
  int x;
  int y;
  int width;
  int height;
  public void SetValue(Rectangle r)
  {
  this.x = r.x;
  this.y = r.y;
  this.width = r.width;
  this.height = r.height;
  }
  public Rectangle(int x, int y, int width, int height)
  {
  this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;
  }
  public void MoveTo(Point p)
  {
  //....
  }
  public void ChangeWidth(int width)
  {
  }
  public void ChangeHeight(int height)
  {
  }
  public void Draw(Graphics graphic)
  {
  }
  internal RectangleMemento Creatememento()
  {
  RectangleMemento memento = new RectangleMemento();
  memento.SetState(this.x, this.y, this.width, this.height);
  return memento;
  }
  internal void SetMemento(RectangleMemento memento)
  {
  this.x = memento.x;
  this.y = memento.y;
  this.width = memento.width;
  this.height = memento.height;
  }
  }
  internal class RectangleMemento
  {
  internal int x;
  internal int y;
  internal int width;
  internal int height;
  internal void SetState(int x, int y, int width, int height)
  {
  this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;
  }
  }
  public class GraphicsSystem
  {
  //原发器对象:
  //有必要对自身内部状态进行保存,然后在某个点处又需要恢复内部状态的对象
  Rectangle r = new Rectangle(0, 0, 10, 10);
  //备忘录对象:
  //保存原发器对象的内部状态,但不提供原发器对象支持的操作
  RectangleMemento rSaved = new RectangleMemento();
  public void Process(Rectangle r)
  {
  rSaved = r.Creatememento();
  //....
  }
  public void Saved_Click(object sender, EventArgs e)
  {
  r.SetMemento(rSaved);
  //....
  }
  }
  在上面这段代码中,我们将备忘录对象抽象出来为RectangleMemento类,这个类只保存了原发器对象基本的值,但没有提供其他的操作,并且,我们将RectangleMemento类和内部成员全部申明为internal只能让程序集本身调用,保证了RectangleMemento对象的封装性。
  实现要点:
  备忘录存储原发器(Originator)对象的内部状态,在需要时恢复原发器状态。Memento模式适用于由原发器管理,却又必须存储在原发器之外的信息
  在实现Memento模式中,要防止原发器以外的对象方位备忘录对象,备忘录对象有两个接口,一个为原发器使用的宽接口,一个为其他对象使用的窄接口
  在实现Memento模式时,要考虑拷贝对象状态的效率问题,如果对象开销比较大,可以采用某种增量式改变来跟进Memnto模式
  在上面的例子中Rectangle对于RectangleMemento看到是宽接口,即SetState方法,而GraphicsSystem看到的是窄接口,即RectangleMemento的构造函数和Creatememento、SetMemento方法。


  当一个对象比较大的时候,在.net中如DataSet,要保存对象的状态可能会造成效率问题,占用比较大的内存。
  下面一段代码演示了通过使用序列化的方式来实现Memento模式
  [Serializable]
  public class Rectangle
  {
  int x;
  int y;
  int width;
  int height;
  public void SetValue(Rectangle r)
  {
  this.x = r.x;
  this.y = r.y;
  this.width = r.width;
  this.height = r.height;
  }
  public Rectangle(int x, int y, int width, int height)
  {
  this.x = x;
  this.y = y;
  this.width = width;
  this.height = height;
  }
  public void MoveTo(Point p)
  {
  //....
  }
  public void ChangeWidth(int width)
  {
  }
  public void ChangeHeight(int height)
  {
  }
  public void Draw(Graphics graphic)
  {
  }
  public GeneralMementor CreateMemento()
  {
  GeneralMementor memento = new GeneralMementor();
  memento.SetState(this);
  return memento;
  }
  public void SetMemento(GeneralMementor memento)
  {
  Rectangle r = memento.GetState() as Rectangle;
  SetValue(r);
  }
  }
  public class GeneralMementor
  {
  MemoryStream rSaved = new MemoryStream();
  internal void SetState(object obj)
  {
  BinaryFormatter bf = new BinaryFormatter();
  bf.Serialize(rSaved, obj);
  }
  internal object GetState()
  {
  BinaryFormatter bf = new BinaryFormatter();
  rSaved.Seek(0, SeekOrigin.End);
  object obj = bf.Deserialize(rSaved);
  return obj;
  }
  }
  public class GraphicsSystem
  {
  //原发器对象:
  //有必要对自身内部状态进行保存,然后在某个点处又需要恢复内部状态的对象
  Rectangle r = new Rectangle(0, 0, 10, 10);
  GeneralMementor memntor = null;
  //备忘录对象:
  //保存原发器对象的内部状态,但不提供原发器对象支持的操作
  public void Process(Rectangle r)
  {
  memntor = r.CreateMemento();
  //....
  }
  public void Saved_Click(object sender, EventArgs e)
  {
  r.SetMemento(memntor);
  //....
  }
  }
  上面的代码中我们可以看到将Rectangle原发器保存在内存流里,在恢复的时候,将内存流里的对象进行还原,我们可以更进一步的抽象,可以将对象保存在任何的流里,这里就不做演示了。

 

文章录入:杜斌    责任编辑:杜斌 
  • 上一篇文章:

  • 下一篇文章:
  • 【字体: 】【发表评论】【加入收藏】【告诉好友】【打印此文】【关闭窗口
     

    联 系 信 息
    QQ:88236621
    电话:15853773350
    E-Mail:malenurse@163.com
    免费发布招聘信息
    做中国最专业男护士门户网站
    最 新 热 门
    最 新 推 荐
    相 关 文 章
    没有相关文章
    专 题 栏 目