行为型模式:Memento 备忘录模式

                                                   行为型模式:Memento 备忘录模式

1、对象状态的回溯
  对象状态的变化无端,如何回溯/恢复对象在某个点的状态?
 
2、动机(Motivation)
  1)在软件构建过程中,某些对象的状态在转换过程中,可能由于某种需要,要求程序能够回溯到对象之前处于某个点时的状态。如果使用一些公有接口来让其他对象得到对象的状态,便会暴露对象的细节实现。
  2)如何实现对象状态的良好保存与恢复?但同时又不会因此而破坏对象本身的封装性。
 
3、意图(Intent)
  在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将该对象恢复到原先保存的状态。
                                                                                                        ——《设计模式》GoF
 
4、实例:保存矩形的状态
  1)很原始的做法,它能够解决这个问题,但破坏封装性
//矩形
public class Rectangle : ICloneable
{
  private int x; //X坐标
  private int y; //Y坐标
  private int width;      //宽
  private int 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 SetValue(Rectangle r)
  {
    this.x = r.x;
    this.y = r.y;
    this.width = r.width;
    this.height = r.height;
  }
 
  //克隆
  public object Clone()
  {
    return this.MemberwiseClone();
  }
 
  //移动矩形
  public void MoveTo(Point p)
  {
  }
 
  //改变宽
  public void ChangWidth(int width)
  {
  }
 
  //改变高
  public ChangeHeight(int height)
  {
  }
 
  //绘制
  public void Draw(Graphic graphic)
  {
  }
}

//操作图形
public class GraphicsSystem
{
  //原发器对象
  //有必要对自身状态进行保存,然后在某个点处又需要恢复的对象
  Rectangle r = = new Rectangle(0, 0, 10, 10);
 
  //备忘录对象,用来保存原发器对象的状态,
  //但是不提供原发器对象支持的操作
  Rectangle rSaved = = new Rectangle(0, 0, 10, 10);
  //rSaved现在有拥有改变他自己属性的方法,所以它也可能被改变
 
  public void Process(Rectangle r)
  {
    rSaved = r.Clone();
    //...
  }
 
  public void Saved_Click(object sender, EventArgs e)
  {
    r.SetValue(rSaved);
  }
}

  2)演变成备忘录Memento模式:不破坏对象的封装性,这就是备忘录模式要解决的问题
//矩形
public class Rectangle
{
  private int x; //X坐标
  private int y; //Y坐标
  private int width;      //宽
  private int 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 ChangWidth(int width)
  {
  }
 
  //改变高
  public ChangeHeight(int height)
  {
  }
 
  //绘制
  public void Draw(Graphic graphic)
  {
  }
 
  //保存
  public RectangMemento CreateMemento()
  {
    RectangleMemento rm = new RectangleMemento();
    rm.SetState(this.x, this.y, this.width, this.height);
    return rm;
  }
 
  //恢复
  public void SetMemento(RectangleMemento rm)
  {
    this.x = rm.x;
    this.y = rm.y;
    this.width = rm.width;
    this.height = rm.height;
  }
}

//新建了这个类,用来保存矩形的状态,只封装状态,不提供其他操作
public class RectangleMemento
{
  internal int x; //X坐标
  internal int y; //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;
  }
}

//操作图形:GraphicsSystem处于另外的程序集中
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);
  }
}

  3)序列化的方式保存状态
[Serializable]
public class Rectangle
{
  private int x; //X坐标
  private int y; //Y坐标
  private int width;      //宽
  private int 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 ChangWidth(int width)
  {
  }
 
  //改变高
  public ChangeHeight(int height)
  {
  }
 
  //绘制
  public void Draw(Graphic graphic)
  {
  }
 
  public void CreateMemento()
  {
    GeneralMementor gm = new GeneralMementor();
    gm.SetState(this);
  }
 
  public void SetMemento(GeneralMementor memento)
  {
    Rectangle r = (Rectangle)memento.GetSatte();
    this.x = r.x;
    this.y = r.y;
    this.width = r.width;
    this.height = r.height;
  }
}

//通用备忘录:适合任何支持序列化的对象
//使用序列化和工厂方法模式配合可以保存在内存中,线程内,线程外,数据库等
public class GeneralMementor
{
  private Stream rSaved;
  public GeneralMemento(Factory factory)
  {
    rSaved = factory.CreateStream();
  }
 
  internal void SetState(object obj)
  {
    //序列化
    BinaryFormatter bf = new BinaryFormatter();
    bf.Serialize(obj, rSaved);
  }
 
  internal object GetState()
  {
    //反序列化
    BinaryFormatter bf = new BinaryFormatter();
    rSaved.Seek(Seek.Original, 0);//将流的游标移到开头
    object obj = (Rectangle)bf.DeSerialize(rSaved);
   
    return obj;
  }
}

5、Memento模式的几个要点
  1)备忘录(Memento)存储原发器(Originator)对象的内部状态,在需要时恢复原发器状态。Memento模式适用于“由原发器管理,却又必须存储在原发器之外的信息”。
  2)在实现Memento模式中,要防止原发器以外的对象访问备忘录对象。备忘录对象有两个接口,一个为原发器使用的宽接口;一个为其他对象使用的窄接口。
  3)在实现Memento模式时,要考虑拷贝对象状态的效率问题,如果对象开销比较大,可以采用某种增量式改变来改进Memento模式。

你可能感兴趣的:(行为型模式:Memento 备忘录模式)