设计模式学习笔记二十一(Memoento备忘录模式)

动机:在软件构建过程中,某些对象的状态在转换过程中,可能由于某种需要,要求程序能够回溯到对象之前处于某个点时的状态。如果使用一些公有接口来让其他对象得到对象的状态,便会暴露对象的细节实现。如何实现对象状态的良好保存与恢复?但同时又不会因此而破坏对象本身的封装性。

意图:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之前保存这个状态,这样以后就可以将该对象恢复到原先保存的状态。

基本Code

//长方形

public class Rectangle:ICloneable

{

    int x;

    int y;

    int weight;

    int height;

    public Rectangle(int x, int y, int weight, int height)

    {

        this.x = x;

        this.y = y;

        this.weight = weight;

        this.height = height;

    }

    public void SetValue(Rectangle r)

    {

        this.x = r.x;

        this.y = r.y;

        this.weight = r.weight;

        this.height = r.height;

    }

    public object Clone()

    {

        return this.MemberwiseClone();

    }

    public void MoveTo(Point p)

    {

    }

    public void ChangeWidth(int width)

    {

        this.weight = width;

    }

    public void ChangeHeight(int height)

    {

        this.height = height;

    }

    public void Draw(Graphics graphic)

    {

    }

}

 

public class GraphicSystem

{

   //原发器对象,有必要对自身内部状态(实例字段)进行保存,然后在某个点处又需要恢复内部状态的对象

    Rectangle r = new Rectangle(0, 0, 10, 10);

    //备忘录对象,保存原发器对象内部状态,但是不提供原发器对象提供的操作,如Clone()

Rectangle rSaved = new Rectangle(0, 0, 10, 10);  

public static void Main()

    {

        Process(r);

    }

  

    public  void Process(Rectangle r)

    {

        rSaved = r.Clone();

       

       //...

    }

    public void Saved_Click(object sender, EventArgs e)

    {

        r.SetValue(rSaved);

    }

}

在上述代码中,rSaved是保存对象状态,而不能对其进行修改,但由于rSaved存在SetValue接口,说明我们可以修改,故不能满足单一职责原则,所以在使用克隆的时候,提供了太多的接口。我们使用Memento模式改写代码如下:

//长方形

public class Rectangle:ICloneable

{

    int x;

    int y;

    int weight;

    int height;

    public Rectangle(int x, int y, int weight, int height)

    {

        this.x = x;

        this.y = y;

        this.weight = weight;

        this.height = height;

    }

    public void MoveTo(Point p)

    {

    }

    public void ChangeWidth(int width)

    {

        this.weight = width;

    }

    public void ChangeHeight(int height)

    {

        this.height = height;

    }

    public void Draw(Graphics graphic)

    {

    }

    //存储备忘录对象

    public RectangleMemoento CreateMemoento()

    {

        RectangleMemoento rm = new RectangleMemoento();

        rm.SetState(this.x, this.y, this.weight, this.height);

        return rm;

    }

    //恢复备忘录对象

    public void SetMemoento(RectangleMemoento rm)

    {

        this.x = rm.x;

        this.y = rm.y;

        this.weight = rm.weight;

        this.height = rm.height;

    }

 

}

 

//长方形备忘录对象

public class RectangleMemoento

{

    internal int x;

    internal int y;

    internal int weight;

    internal int height;

    internal void  SetState(int x, int y, int weight, int height)

    {

        this.x = x;

        this.y = y;

        this.weight = weight;

        this.height = height;

    }

}

 

//在另外一个程序集中

public class GraphicSystem

{

    //原发器对象,有必要对自身状态进行保存,然后在某个点处又需要恢复的对象

    Rectangle r = new Rectangle(0, 0, 10, 10);

    //备忘录对象,保存原发器对象状态,但是不提供原发器对象提供的操作,如SetValue

    RectangleMemoento  rSaved;

  

    public  void Process(Rectangle r)

    {

        rSaved = r.CreateMemoento();

       

       //...

    }

    public void Saved_Click(object sender, EventArgs e)

    {

        r.SetMemoento(rSaved);

    }

}

 

Memoento模式结构图如下:

 

Memento模式要点:

1、  备忘录(Memento)存储原发器(Originator)对象的内部状态,在需要时恢复原发器状态。Memento模式适用于“由原发器管理,却又必须存储在原发器之外的信息”。

2、  在实现Memento模式中,要防止原发器以外的对象访问备忘录对象。备忘录对象有两个接口,一个为原发器使用的宽接口;一个为其他对象使用的窄接口。

3、  在实现Memento模式时,要考虑拷贝对象状态的效率问题,如果对象开销比较大,可以采用某种增量式改变来改进Memento模式。

 

使用序列化的方式的Memento模式Code如下:

//长方形

[Serializable]

public class Rectangle : ICloneable

{

    int x;

    int y;

    int weight;

    int height;

    public Rectangle(int x, int y, int weight, int height)

    {

        this.x = x;

        this.y = y;

        this.weight = weight;

        this.height = height;

    }

    public void MoveTo(Point p)

    {

    }

    public void ChangeWidth(int width)

    {

        this.weight = width;

    }

    public void ChangeHeight(int height)

    {

        this.height = height;

    }

    public void Draw(Graphics graphic)

    {

    }

    //存储备忘录对象

    public GeneralMemoento CreateGeneralMemoentor()

    {

        GeneralMemoentor gm = new GeneralMemoentor();

        gm.SetState(this);

        return gm;

    }

    //恢复备忘录对象

    public void SetMemoento(GeneralMemoento gm)

    {

        Rectangle r = (Rectangle)gm.GetState();

        this.x = r.x;

        this.y = r.y;

        this.weight = r.weight;

        this.height = r.height;

    }

}

 

//采用抽象工厂可以支持多种流

public abstract class Factory

{

    public Stream CreateStream();

}

 

 

public class StreamFactory:Factory

{

    public override Stream CreateStream()

    {

        return new MemoryStream();

    }

}

//采用序列化做的通用Memoento备忘录对象

public class GeneralMemoento

{

    Stream rSaved;

    public GeneralMemoento(Factory factory)

    {

        rSaved = factory.CreateStream();

    }

    internal void SetState(object obj)

    {

        BinaryFormatter bf = new BinaryFormatter();

        bf.Serialize(rSaved, obj);

    }

    internal object GetState()

    {

        BinaryFormatter bf = new BinaryFormatter();

        rSaved.Seek(0, SeekOrigin.Begin);

        object obj = bf.Deserialize(rSaved);

        return obj;

    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值