意图
在不破坏封装性的前提条件下,捕获一个对象的内部状态,然后在该对象之外保存这个状态。以后在需要的时候可以将该对象恢复到原先保存的状态。
结构
1.Memento(备忘录):保存Originator(原发器)对象的内部状态,Originator根据需要决定保存哪些内部状态,防止自身以外的其它对象访问备忘录。备忘录实际上是由两个接口,其中Caretaker(管理者)只能看到备忘录的窄接口,即它只能将备忘录传递给其他对象;而原发器可以看到一个宽接口,允许他访问回到原先状态所需的所有数据,理想的情况是只允许生成原发器访问本备忘录的内部状态。
2.Originator:创建一个备忘录以记录当前时刻内部状态,使用备忘录恢复内部状态。
3.Caretaker:负责保存备忘录,但不能处理其中的内容。
使用场合
需要保存对象在某一时刻的状态,并在以后需要的时候恢复到这个状态。同时又不希望暴露对象的视线细节,破坏对象的封装性,这时需要使用备忘录模式。
效果
备忘录模式在不破坏封装性的前提下,实现对对象内部状态的外部保存。但如果保存的状态过多,或者设计不合理,则将产生过多的备忘录对象而占用大量的系统资源。
备忘录模式的基本框架
备忘录模式的难点在于在确保封装的前提下,将内部状态放到外部保存。如果单纯从保存状态出发(即宽接口方式),备忘录并不难实现。
宽接口实现备忘录模式
下面是Originator的代码框架:
using System.Collections.Generic;
using System.Text;
namespace MemoPattern.ex28_3
{
class Originator
{
private State state;
public Originator()
{
state=new State();
state.Level = 1;
state.StateName = "Bejin";
}
public string StateName
{
get { return state.StateName; }
set { state.StateName = value; }
}
public int Level
{
get { return state.Level; }
set { state.Level = value; }
}
public Memo CreateMemo()
{
return new Memo(this.state);
}
public void SetState(Memo m)
{
this.state=m.State;
}
}
}
State是保存状态的类:
using System.Collections.Generic;
using System.Text;
namespace MemoPattern.ex28_3
{
class State:ICloneable
{
public string StateName="";
public int Level=-1;
ICloneable メンバ
}
}
Memonto类如下:
在保存状态时需要注意传值与传地址的区别。要保存的是新的状态对象而非原状态对象的引用。
可以使用原型模式,使State有Clone功能:
state=st.clone();
using System.Collections.Generic;
using System.Text;
namespace MemoPattern.ex28_3
{
class Memo
{
private State _state;
public Memo(State state)
{
_state = (State)state.Clone();
}
public State State
{
get { return _state; }
set { _state = value; }
}
}
}
Caretaker代码如下:
使用强类型集合存储备忘录。
using System.Collections.Generic;
using System.Text;
namespace MemoPattern.ex28_3
{
class Caretaker:List<Memo>
{
public Memo Restore()
{
if(Count==0)return null;
Memo m = this[Count - 1];
RemoveAt(Count-1);
return m;
}
}
}
执行代码如下:
{
ex28_3.Originator o=new MemoPattern.ex28_3.Originator();
ex28_3.Caretaker c = new MemoPattern.ex28_3.Caretaker();
c.Add(o.CreateMemo());
o.StateName = "one";
o.Level = 100;
c.Add(o.CreateMemo());
o.StateName = "two";
o.Level = 200;
c.Add(o.CreateMemo());
ex28_3.Memo m = c.Restore();
while (m != null)
{
o.SetState(m);
Console.WriteLine(o.StateName);
m = c.Restore();
}
}
这种方式状态State对外是透明的,因此状态都可以被修改。这并不符合备忘录模式的要求。下面将采用接口屏蔽的方式完善以上代码。
接口屏蔽实现备忘录模式
首先定义一个接口,其目的是屏蔽Memonto中的细节。
using System.Collections.Generic;
using System.Text;
namespace MemoPattern.ex28_4
{
public class IMemo
{
}
}
改造Originator
我们将Memonto作为Originator的私有类,这一步非常重要。
using System.Collections.Generic;
using System.Text;
namespace MemoPattern.ex28_4
{
class Originator
{
private State state;
public Originator()
{
state=new State();
state.Level = 1;
state.StateName = "Bejin";
}
public string StateName
{
get { return state.StateName; }
set { state.StateName = value; }
}
public int Level
{
get { return state.Level; }
set { state.Level = value; }
}
public IMemo CreateMemo()
{
return new Memo(this.state);
}
public void SetState(IMemo m)
{
this.state = ((Memo)m).State;
}
private class Memo:IMemo
{
private State _state;
public Memo(State st)
{
_state = (State)st.Clone();
}
public State State
{
get { return _state; }
set { _state = value; }
}
}
}
}
改造Caretaker
using System.Collections.Generic;
using System.Text;
namespace MemoPattern.ex28_4
{
class Caretaker:List<IMemo>
{
public IMemo Restore()
{
if(Count==0)return null;
IMemo m = this[Count - 1];
RemoveAt(Count-1);
return m;
}
}
}
实现代码:
{
ex28_4.Originator o = new MemoPattern.ex28_4.Originator();
ex28_4.Caretaker c = new MemoPattern.ex28_4.Caretaker();
c.Add(o.CreateMemo());
o.StateName = "one";
o.Level = 100;
c.Add(o.CreateMemo());
o.StateName = "two";
o.Level = 200;
c.Add(o.CreateMemo());
ex28_4.IMemo m = c.Restore();
while (m != null)
{
o.SetState(m);
Console.WriteLine(o.StateName);
m = c.Restore();
}
}
相关模式
1.命令模式:别忘录可以作为撤销操作维护状态。
2.迭代器: 备忘录可以用于迭代器,为期提供迭代状态。