备忘录模式(Memento)
本质:保存和恢复内部状态
Memento模式的关键就是要在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以利用该保存的状态实施恢复操作
Originator(原发器):
创建一个备忘录,并存储它的当前内部状态,也可以使用备忘录来恢复其内部状态。
Memento(备忘录):
存储原发器的内部状态,备忘录的设计一般可以参考原发器的设计,根据实际需要确定备忘录类中的属性。
Caretaker(负责人):
负责人又称为管理者,在负责人类中可以存储一个或多个备忘录对象,它只负责存储对象,而不能修改对象,也无须知道对象的实现细节。
class Originator
{
private string state;
public string State
{
get { return state; }
set { state = value; }
}
public Memento CreateMemento()
{
return (new Memento(state));
}
public void SetMemento(Memento memento)
{
state = memento.State;
}
public void Show()
{
Console.WriteLine("State=" + state);
}
}
class Memento
{
private string state;
public Memento(string state)
{
this.state = state;
}
public string State
{
get { return state; }
}
}
class Caretaker
{
private Memento memento;
public Memento Memento
{
get { return memento; }
set { memento = value; }
}
}
class Program
{
static void Main(string[] args)
{
Originator o = new Originator();
o.State = "On";
o.Show();
Caretaker c = new Caretaker();
c.Memento = o.CreateMemento();
o.State = "Off";
o.Show();
o.SetMemento(c.Memento);
o.Show();
Console.Read();
}
}
优缺点
优点:
- 有时一些发起人对象的内部信息必须保存在发起人对象以外的地方,但是必须要由发起人对象自己读取,这时,使用备忘录模式可以把复杂的发起人内部信息对其他的对象屏蔽起来,从而可以恰当地保持封装的边界。
- 本模式简化了发起人类。发起人不再需要管理和保存其内部状态的一个个版本,客户端可以自行管理他们所需要的这些状态的版本。
- 当发起人角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原。
缺点:
- 如果发起人角色的状态需要完整地存储到备忘录对象中,那么在资源消耗上面备忘录对象会很大。
- 当管理者角色将一个备忘录存储起来的时候,管理者可能并不知道这个状态会占用多大的存储空间,从而无法提醒用户一个操作是否很大。
适用场合:
如果必须保存一个对象在某一个时刻的全部或者部分状态,方便在以后需要的时侯,可以把该对象恢复到先前的状态,可以使用备忘录模式
例子——游戏进度存档
class GameRole
{
private int vit; //生命力
public int Vitality
{
get { return vit; }
set { vit = value; }
}
private int atk; //攻击力
public int Attack
{
get { return atk; }
set { atk = value; }
}
private int def; //防御力
public int Defense
{
get { return def; }
set { def = value; }
}
public void StateDisplay()
{
Console.WriteLine("角色当前状态:");
Console.WriteLine("体力:{0}", this.vit);
Console.WriteLine("攻击力:{0}", this.atk);
Console.WriteLine("防御力:{0}", this.def);
Console.WriteLine("");
}
//保存角色状态
public RoleStateMemento SaveState()
{
return (new RoleStateMemento(vit, atk, def));
}
//恢复角色状态
public void RecoveryState(RoleStateMemento memento)
{
this.vit = memento.Vitality;
this.atk = memento.Attack;
this.def = memento.Defense;
}
//获得初始状态
public void GetInitState()
{
this.vit = 100;
this.atk = 100;
this.def = 100;
}
//战斗
public void Fight()
{
this.vit = 0;
this.atk = 0;
this.def = 0;
}
}
class RoleStateMemento
{
private int vit;
private int atk;
private int def;
public RoleStateMemento(int vit, int atk, int def)
{
this.vit = vit;
this.atk = atk;
this.def = def;
}
//生命力
public int Vitality
{
get { return vit; }
set { vit = value; }
}
//攻击力
public int Attack
{
get { return atk; }
set { atk = value; }
}
//防御力
public int Defense
{
get { return def; }
set { def = value; }
}
}
class RoleStateCaretaker
{
private RoleStateMemento memento;
public RoleStateMemento Memento
{
get { return memento; }
set { memento = value; }
}
}
static void Main(string[] args)
{
//大战Boss前
GameRole lixiaoyao = new GameRole();
lixiaoyao.GetInitState();
lixiaoyao.StateDisplay();
//保存进度
RoleStateCaretaker stateAdmin = new RoleStateCaretaker();
stateAdmin.Memento = lixiaoyao.SaveState();
//大战Boss时,损耗严重
lixiaoyao.Fight();
lixiaoyao.StateDisplay();
//恢复之前状态
lixiaoyao.RecoveryState(stateAdmin.Memento);
lixiaoyao.StateDisplay();
Console.Read();
}
例子——象棋中“悔棋”的实现
Chessman充当原发器,ChessmanMemento充当备忘录,MementoCaretaker充当负责人,在MementoCaretaker中定义了一个ChessmanMemento类型的对象,用于存储备忘录
package Memento;
import java.util.ArrayList;
public class CareTaker {
private ArrayList<ChessMemento> list = null;
public CareTaker() {
list = new ArrayList<ChessMemento>();
}
public void addMemento(ChessMemento c) {
list.add(c);
}
public ChessMemento huiQi() {
if (list.isEmpty()) {
System.out.println("无法悔棋");
return null;
} else {
int index = list.size();
ChessMemento c = (ChessMemento) list.get(index - 1);
list.remove(index - 1);
return c;
}
}
}
package Memento;
public class ChessMemento {
private int x;
private int y;
private int id;
private String label;
public ChessMemento(int x, int y,int id, String label) {
this.x = x;
this.y = y;
this.id=id;
this.label = label;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
@Override
public String toString() {
return "棋子" + this.label +"id="+this.id+ "当前位置为(" + x + "," + y + ")";
}
}
package Memento;
public class Originator {
private int x;
private int y;
private int id;
private String label;
public Originator(int x, int y, int id,String label) {
this.x = x;
this.y = y;
this.id=id;
this.label = label;
}
public void setState(int x,int y){
this.x = x;
this.y = y;
}
public ChessMemento CreatMemento(){
return new ChessMemento(x,y,id,label);
}
public void getMemento(ChessMemento c){
if(c==null)return;
this.x = c.getX();
this.y = c.getY();
this.id=c.getId();
this.label = c.getLabel();
}
public void show(){
System.out.println(CreatMemento());
}
}
package Memento;
public class Main {
public static void main(String[] args) {
CareTaker ct = new CareTaker();
ct.huiQi();
Originator o = new Originator(0,0,2,"车");
o.show();
ct.addMemento(o.CreatMemento());
o.setState(1, 5);
o.show();
System.out.println("现在悔棋");
o.getMemento(ct.huiQi());
o.show();
o.setState(8, 9);
o.show();
ct.addMemento(o.CreatMemento());
o.setState(5, 6);
o.show();
ct.addMemento(o.CreatMemento());
o.setState(3, 7);
o.show();
System.out.println("现在悔棋");
o.getMemento(ct.huiQi());
o.show();
System.out.println("现在悔棋");
o.getMemento(ct.huiQi());
o.show();
System.out.println("现在悔棋");
o.getMemento(ct.huiQi());
o.show();
}
}
备忘录模式和原型模式
这两个模式可以组合使用.
在原发器对象(发起人对象)创建备忘录对象的时候,如果原发器对象中全部或者大部分的状态都需要保存,一个简洁的方式就是直接克隆一个原发器对象。也就是说,这个时候备忘录对象里面存放的是一个原发器对象的实例。