设计模式--Memento 备忘录模式
在软件的构建过程中,某些对象的状态在转换过程中,可能由于某种需要,要求程序能够回溯到对象之前某个点时的状态,如果使用一些公有接口来让其他对象得到对象的状态,便会暴露对象的细节实现。
如何实现对象状态的良好保存与恢复?但同时又不会因此而破坏对象本身的封装性。
Memento 备忘录模式提供解决途径,它在不破坏封装性的前提下,捕获一个对象的内部状态,并在这个对象之外保存这个状态。这样就可以将对象恢复到原先保存的状态。《设计模式》— GOF
Memento备忘录模式UML图如下:
主要角色:
1、原发器角色Originator:它是我们关注的对象,我们需要保存和回溯的状态就是它的状态。我们需要在它内部创建备忘录对象并利用备忘录对象保存我们需要保存的状态值,同时它还需要提供一种手段来恢复我们以前保存的状态值.
2、备忘录对象Memento:它用于在不破坏封装性的前提下,捕获一个Originator的内部状态,并在备忘录对象Memento中保存这个状态。(Caretaker:用于防止原发器以外的对象访问备忘录对象,保证备忘录对象的安全性)。
下面我们用代码来示例,程序如下:
一、备忘录模式思路示例
1、原发器角色Originator
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyMementoPattern
{
//Memento模式适用于由原发器管理,却又必须存储在原发器之外的信息
//定义原发器对象:Originator
//有必要对自身内部状态进行保存,然后在某个点处又需要恢复内部状态的对象
class Originator
{
#region State属性
private string _state;
public string State
{
get { return _state; }
set {
_state = value;
Console.WriteLine("State={0}",_state);
}
}
#endregion
//在实现Memento模式中,要防止原发器Originator以外的对象访问备忘录对象,备忘录对象有两个接口,一个为原发器使用的宽接口,一个为其他对象使用的窄接口
#region 创建Memento类的方法(Memento类将用于保存Originator对象的State状态值)
public Memento CreateMemento()
{
Console.WriteLine("创建Memento对象并保存状态到此对象中
![](https://www.cnblogs.com/Images/dot.gif)
return (new Memento(_state)); //此处,在创建Memento对象时就保存了对象状态
}
#endregion
#region 利用上面Memento对象保存的状态值进行状态恢复操作
public void SetMemento(Memento memnto)
{
Console.WriteLine("恢复对象状态
![](https://www.cnblogs.com/Images/dot.gif)
State = memnto.State;
}
#endregion
}
}
2、备忘录对象Memento
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyMementoPattern
{
class Memento
{
//定义备忘录对象Memento类,它专门用于保存Originator类对象的状态信息
//也即:在不破坏封装性的前提下,捕获一个Originator的内部状态,并在这个对象之外保存这个状态。
//而这个状态值就保存在Memento类中
private string _state;
public string State
{
get { return _state; }
}
#region 构造函数
public Memento(string state)
{
this._state = state;
}
#endregion
}
}
3、Caretaker
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyMementoPattern
{
//定义CareTaker类
//此类的作用是保证Mmento类的安全性
class CareTaker
{
private Memento _memento;
public Memento Memento
{
get { return _memento; }
set { _memento = value; }
}
}
}
4、客户端应用
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
#region 基本思路示例
Console.WriteLine("-------------备忘录模式基本思路示例---------------");
Originator o = new Originator();
o.State = "On"; //设置Originator类对象状态
CareTaker c = new CareTaker();
c.Memento = o.CreateMemento();
Console.WriteLine("设置状态新值
![](https://www.cnblogs.com/Images/dot.gif)
o.State = "Off";
o.SetMemento(c.Memento);
Console.ReadKey();
#endregion
二、在客户对象上使用备忘录模式
1、原发器角色Originator:ClientsOriginator
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyMementoPattern
{
class ClientsOriginator
{
#region 姓名属性
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
Console.WriteLine("姓名: " + _name);
}
}
#endregion
#region 电话属性
private string _phone;
public string Phone
{
get { return _phone; }
set
{ _phone = value;
Console.WriteLine("电话: " + _phone);
}
}
#endregion
#region 住址属性
private string _address;
public string Address
{
get { return _address; }
set
{
_address = value;
Console.WriteLine("住址: " + _address);
}
}
#endregion
public ClientMemento SaveMemento()
{
Console.WriteLine("\n 保存客户状态
![](https://www.cnblogs.com/Images/dot.gif)
return (new ClientMemento(_name,_phone,_address));
}
public void RestoreMemento(ClientMemento memento)
{
Console.WriteLine("\n 恢复客户状态值
![](https://www.cnblogs.com/Images/dot.gif)
![](https://www.cnblogs.com/Images/dot.gif)
this.Name = memento.Name;
this.Phone = memento.Phone;
this.Address = memento.Address;
}
}
}
2、备忘录对象Memento:ClientsMemento
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyMementoPattern
{
class ClientMemento
{
#region 姓名属性
private string _name;
public string Name
{
get { return _name; }
set {_name = value; }
}
#endregion
#region 电话属性
private string _phone;
public string Phone
{
get { return _phone; }
set { _phone = value; }
}
#endregion
#region 住址属性
private string _address;
public string Address
{
get { return _address; }
set { _address = value; }
}
#endregion
public ClientMemento(string name,string phone, string address)
{
this._name = name;
this._phone = phone;
this._address = address;
}
}
}
3、Caretaker:ClientMemory
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MyMementoPattern
{
class ClientMemory
{
private ClientMemento memento;
public ClientMemento Memento
{
get { return memento; }
set { memento = value; }
}
}
}
4、客户端应用
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
#region 客户状态示例
Console.WriteLine("-------------在客户对象上使用备忘录模式---------------");
ClientsOriginator co = new ClientsOriginator();
co.Name = "王军";
co.Phone = "86150338";
co.Address = "北京";
ClientMemory cm = new ClientMemory();
cm.Memento = co.SaveMemento();
Console.WriteLine("\n 修改客户状态
![](https://www.cnblogs.com/Images/dot.gif)
co.Name = "王小军";
co.Phone = "99999999";
co.Address = "深圳";
co.RestoreMemento(cm.Memento);
Console.ReadKey();
#endregion
运行效果如下:
总结:
备忘录存储原发器(Originator)对象的内部状态,在需要时恢复原发器状态。Memento模式适用于由原发器管理,却又必须存储在原发器之外的信息
在实现Memento模式中,要防止原发器以外的对象方位备忘录对象,备忘录对象有两个接口,一个为原发器使用的宽接口,一个为其他对象使用的窄接口。在 上面的例子中Originator对于Memento看到是宽接口,即SetState方法,而用户端看到的是窄接口,即Memento的构造函数和 Creatememento、SetMemento方法。
在实现Memento模式时,要考虑拷贝对象状态的效率问题,如果对象开销比较大,可以采用某种增量式改变来跟进Memnto模式