备忘录模式
一、备忘录模式
备忘录模式(Memento Pattern)是一种对象行为型模式,又叫快照模式,别名为Token。
定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。
使用频率:2星
模式结构:
备忘录模式的主要角色如下:
1、发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
2、备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
3、管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。
备忘录模式结构图:
优点:
1、提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。
2、实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
3、简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。
缺点:
资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。
应用场景:
1、需要保存与恢复数据的场景,如玩游戏时的中间结果的存档功能。
2、需要提供一个可回滚操作的场景,如 Word、记事本、Photoshop,Eclipse 等软件在编辑时按 Ctrl+Z 组合键,还有数据库中事务操作。
二、备忘录模式实例之游戏恢复点设置
1.实例说明
某游戏为了给玩家提供更好的用户体验,在游戏过程中设置一个恢复点,记录当前游戏场景,如果在后续游戏中玩家角色“不幸牺牲”,可以返回到先前场景。使用备忘录模式设计该功能。
2.实例类图
3.实例代码
本例中,GameScene充当原发器角色,它是待保存历史状态的类;SceneMemento充当备忘录角色,它存储了GameScene的历史状态;Caretaker充当负责人角色,它用于管理备忘录。
//游戏场景类:原发器
class GameScene{
private String scene;
private int lifeValue;
public void restore(SceneMemento m){
this.scene=m.getScene();
this.lifeValue=m.getLifeValue();
}
public SceneMemento save(){
return new SceneMemento(this.scene,this.lifeValue);
}
public void display(){
System.out.println("当前游戏场景为:"+this.scene+",");
System.out.println("您还有"+this.lifeValue+"条命");
}
// Getter & Setter
public String getScene() {
return scene;
}
public void setScene(String scene) {
this.scene = scene;
}
public int getLifeValue() {
return lifeValue;
}
public void setLifeValue(int lifeValue) {
this.lifeValue = lifeValue;
}
}
//场景备忘录:备忘录
class SceneMemento{
private String scene;
private int lifeValue;
public SceneMemento(String scene, int lifeValue) {
this.scene = scene;
this.lifeValue = lifeValue;
}
// Getter & Setter
public String getScene() {
return scene;
}
public void setScene(String scene) {
this.scene = scene;
}
public int getLifeValue() {
return lifeValue;
}
public void setLifeValue(int lifeValue) {
this.lifeValue = lifeValue;
}
}
//负责人
class Caretaker{
private SceneMemento memento;
// Getter & Setter
public SceneMemento getMemento() {
return memento;
}
public void setMemento(SceneMemento memento) {
this.memento = memento;
}
}
客户端测试:
public class Client {
public static void main(String[] args) {
GameScene scene = new GameScene();
Caretaker ct = new Caretaker();
scene.setScene("无名湖");
scene.setLifeValue(3);
System.out.println("原始状态");
scene.display();
ct.setMemento(scene.save());//原始状态存档
System.out.println("------------------");
scene.setScene("魔鬼洞");
scene.setLifeValue(0);
System.out.println("牺牲状态");
scene.display();
System.out.println("------------------");
scene.restore(ct.getMemento());//加载原始状态存档
System.out.println("恢复到原始状态");
scene.display();
System.out.println("------------------");
}
}
运行结果:
原始状态
当前游戏场景为:无名湖,
您还有3条命
------------------
牺牲状态
当前游戏场景为:魔鬼洞,
您还有0条命
------------------
恢复到原始状态
当前游戏场景为:无名湖,
您还有3条命
------------------
原发器GameScene调用save方法后将产生一个备忘录对象,该备忘录对象将保存在Caretaker中,原发器需要恢复状态时再将其从Caretaker中取出,可以通过调用restore方法来获取存储在备忘录中的状态信息。真实开发中,除了原发器可以创建备忘录并赋值外,其他对象不应该直接调用备忘录中的方法,也不能创建备忘录。如果备忘录被原发器以外的第三者改动,则原发器恢复到历史状态不是真实的历史状态,而是修改过的,这违背了备忘录模式的设计初衷,因此需要对备忘录进行封装。
参考文献
【1】备忘录模式(详解版)
【2】设计模式实训教程(第2版) 刘伟 编著 清华大学出版社