行爲型模式: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;
  }
}this

//新建了這個類,用來保存矩形的狀態,只封裝狀態,不提供其餘操做
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;
  }
}orm

//通用備忘錄:適合任何支持序列化的對象
//使用序列化和工廠方法模式配合能夠保存在內存中,線程內,線程外,數據庫等
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模式。接口

相關文章
相關標籤/搜索