备忘录模式 (Memento Pattern)【使用频率:★★☆☆☆】
1. 概述
在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。
2. 模式中的角色
2.1 Originator(原发器):它是一个普通类,可以创建一个备忘录,并存储它的当前内部状态,也可以使用备忘录来恢复其内部状态,一般将需要保存内部状态的类设计为原发器。
2.2 Memento(备忘录):存储原发器的内部状态,根据原发器来决定保存哪些内部状态。备忘录的设计一般可以参考原发器的设计,根据实际需要确定备忘录类中的属性。需要注意的是,除了原发器本身与负责人类之外,备忘录对象不能直接供其他类使用,原发器的设计在不同的编程语言中实现机制会有所不同。
2.3 Caretaker(负责人):负责人又称为管理者,它负责保存备忘录,但是不能对备忘录的内容进行操作或检查。在负责人类中可以存储一个或多个备忘录对象,它只负责存储对象,而不能修改对象,也无须知道对象的实现细节。
为了实现备忘录模式的封装,我们需要对备忘录的访问做些控制:
对原发器:可以访问备忘录里的所有信息。
对负责人:不可以访问备忘录里面的数据,但是他可以保存备忘录并且可以将备忘录传递给其他对象。
其他对象:不可访问也不可以保存,它只负责接收从负责人那里传递过来的备忘录同时恢复原发器的状态。
3. 模式解读
3.1 模式的类图
3.2 代码实现
以游戏挑战BOSS为实现场景,在挑战BOSS之前,角色的血量、蓝量都是满值,然后存档,在大战BOSS时,由于操作失误导致血量和蓝量大量损耗,所以只好恢复到刚刚开始的存档点,继续进行大战BOSS。
using System;
namespace ConsoleApplication1
{
class Class30
{
public static void Main(string[] args)
{
//打BOSS之前:血、蓝全部满值
Role role = new Role(100, 100);
Console.WriteLine("----------准备----------");
role.Display();
//保持进度
Caretaker caretaker = new Caretaker();
caretaker.setMemento(role.SaveMemento());
//大战BOSS,快come Over了
role.setBloodFlow(20);
role.setMagicPoint(20);
Console.WriteLine("----------战斗----------");
role.Display();
//恢复存档
role.RestoreMemento(caretaker.getMemento());
Console.WriteLine("----------恢复----------");
role.Display();
Console.ReadLine();
}
}
// 原发器
public class Role
{
private int bloodFlow;
private int magicPoint;
public Role(int bloodFlow, int magicPoint)
{
this.bloodFlow = bloodFlow;
this.magicPoint = magicPoint;
}
public int getBloodFlow()
{
return bloodFlow;
}
public void setBloodFlow(int bloodFlow)
{
this.bloodFlow = bloodFlow;
}
public int getMagicPoint()
{
return magicPoint;
}
public void setMagicPoint(int magicPoint)
{
this.magicPoint = magicPoint;
}
// 展示角色当前状态
public void Display()
{
Console.WriteLine("用户当前状态:");
Console.WriteLine("血量:" + getBloodFlow() + ";蓝量:" + getMagicPoint());
}
// 保持存档、当前状态
public Memento SaveMemento()
{
return new Memento(getBloodFlow(), getMagicPoint());
}
// 恢复存档
public void RestoreMemento(Memento memento)
{
this.bloodFlow = memento.getBloodFlow();
this.magicPoint = memento.getMagicPoint();
}
}
// 备忘录
public class Memento
{
private int bloodFlow;
private int magicPoint;
public int getBloodFlow()
{
return bloodFlow;
}
public void setBloodFlow(int bloodFlow)
{
this.bloodFlow = bloodFlow;
}
public int getMagicPoint()
{
return magicPoint;
}
public void setMagicPoint(int magicPoint)
{
this.magicPoint = magicPoint;
}
public Memento(int bloodFlow, int magicPoint)
{
this.bloodFlow = bloodFlow;
this.magicPoint = magicPoint;
}
}
// 负责人
public class Caretaker
{
Memento memento;
public Memento getMemento()
{
return memento;
}
public void setMemento(Memento memento)
{
this.memento = memento;
}
}
}
输出结果:
4、模式的优缺点
4.1 优点
(1)它提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。
(2)备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。
4.2 缺点
资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源。
5、模式适用场景
(1)保存一个对象在某一个时刻的全部状态或部分状态,这样以后需要时它能够恢复到先前的状态,实现撤销操作。
(2)防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象。