概述
基本介绍
1、备忘录模式(Memento Pattern):在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将该对象恢复带原先保存的状态。备忘录模式属于行为型模式。
2、备忘录模式提供了一种状态恢复的实现机制,使得用户可以方便的回退到一个特点的历史步骤,当新的状态无效或者存在问题时,可以使用暂存起来的备忘录将其恢复。
3、备忘录对象主要用来记录一个对象的某种状态,或者默写数据,可以从备忘录对象中获取原来的数据进行恢复操作,比如:在象棋对局中悔棋的操作、游戏中节点的保存等等,可以利用备忘录模式。
备忘录模式原理类图分析:
角色分析
1、Originator:源类,是一个普通类,可以创建一个备忘录类,并且存储它的当前状态,也可以使用备忘录来恢复其内部状态,一般需要保存内部状态的类设计为源类。
2、Memento:备忘录,存储源类的内部状态,根据源类来决定保存那些内部状态,需要注意的是,除了源类本身和负责人类,备忘录对象不能直接供其它类使用。
3、Caretaker:负责人类,也叫做管理者,它负责保存备忘录,但是不能对备忘录的内容进行检查和操作,负责人类中可以存储一个或多个备忘录对象,只负责存储对象,不能修改也无需知道对象的实现细节。
代码模拟原理类图
package com.example.pattern.memento;
import lombok.Getter;
import lombok.Setter;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
/**
* 备忘录模式
*/
@Setter
@Getter
public class Originator { // 源类
private String name; // 内部属性
private String status; // 状态
public Originator(String name, String status) {
this.name = name;
this.status = status;
}
public Memento createMemento() { // 保存备忘录
return new Memento(this);
}
public void resetMemento(Memento memento) { // 根据备忘录对象恢复源类状态
this.status = memento.getStatus();
}
@Override
public String toString() {
return "Originator{" +
"name='" + name + '\'' +
", status='" + status + '\'' +
'}';
}
}
@Getter
class Memento { // 备忘录类 不对外提供操作
private String status;
public Memento(Originator originator) {
this.status = originator.getStatus();
}
}
class Caretaker { // 管理类 守护着对象
private List<Memento> mementoList;
public void add(Memento memento) {
if (CollectionUtils.isEmpty(mementoList)) {
mementoList = new ArrayList<>();
}
mementoList.add(memento);
}
public Memento getMementoByIndex(int index) {
if (CollectionUtils.isEmpty(mementoList)) {
throw new RuntimeException("备忘录列表为空");
}
return mementoList.get(index);
}
}
class Client {
public static void main(String[] args) {
Caretaker caretaker = new Caretaker();
Originator o = new Originator("张三", "状态1");
System.out.println(o);
caretaker.add(o.createMemento());
// 变更为状态2
o.setStatus("状态2");
caretaker.add(o.createMemento());
System.out.println(o);
// 变更为状态3
o.setStatus("状态3");
caretaker.add(o.createMemento());
System.out.println(o);
// 恢复到状态1
o.resetMemento(caretaker.getMementoByIndex(0));
System.out.println(o);
}
}
案例实现
游戏角色存档问题
package com.example.pattern.memento.improve;
import lombok.Getter;
import lombok.Setter;
import org.springframework.util.CollectionUtils;
import java.util.HashMap;
import java.util.Map;
/**
* 使用备忘录模式实现游戏角色存档问题
*
* @author zjt
* @date 2021-01-05
*/
@Getter
@Setter
public class GameRole { // 源类
private String roleName; // 角色名称
private String gameLevel; // 游戏关卡
private float HP; // 生命值
private float MP; // 法力值
public GameRole(String roleName, String gameLevel, float HP, float MP) {
this.roleName = roleName;
this.gameLevel = gameLevel;
this.HP = HP;
this.MP = MP;
}
public GameMemento createGameMemento() { // 创建备忘录
return new GameMemento(this);
}
public void recoverGameMemento(GameMemento memento) {
this.roleName = memento.getRoleName();
this.gameLevel = memento.getGameLevel();
this.HP = memento.getHP();
this.MP = memento.getMP();
}
public void exit() {
this.roleName = null;
this.gameLevel = null;
this.MP = 0;
this.HP = 0;
}
@Override
public String toString() {
return "GameRole{" +
"roleName='" + roleName + '\'' +
", gameLevel='" + gameLevel + '\'' +
", HP=" + HP +
", MP=" + MP +
'}';
}
}
@Getter
class GameMemento { // 备忘录类
private String roleName; // 角色名称
private String gameLevel; // 游戏关卡
private float HP; // 生命值
private float MP; // 法力值
public GameMemento(GameRole gameRole) {
this.roleName = gameRole.getRoleName();
this.gameLevel = gameRole.getGameLevel();
this.HP = gameRole.getHP();
this.MP = gameRole.getMP();
}
}
class GameCaretaker { // 备忘录管理类
// 只保存最近一次的存档记录,
private Map<String, GameMemento> mementoMap = new HashMap<>();
public void addGameMemento(GameMemento memento) { // 添加备忘录 添加存档信息
if (null != memento) {
mementoMap.put(memento.getRoleName(), memento);
}
}
public GameMemento getGameMemento(String roleName) { // 根据角色名称获取存档信息
if (CollectionUtils.isEmpty(mementoMap)) {
throw new RuntimeException("存档读取失败");
}
GameMemento memento = mementoMap.get(roleName);
if (memento == null) {
throw new RuntimeException("存档读取失败");
}
return memento;
}
}
class GameClient {
public static void main(String[] args) {
// 创建备忘录管理者
GameCaretaker caretaker = new GameCaretaker();
// 创建用户角色
GameRole role1 = new GameRole("张三", "1-1", 100, 100);
role1.setGameLevel("3-4");
role1.setHP(50);
role1.setMP(47);
caretaker.addGameMemento(role1.createGameMemento());
System.out.println(role1);
System.out.println("用户使用张三退出了游戏");
role1.exit();
System.out.println(role1);
System.out.println("--------------");
System.out.println("用户重新进入了游戏,并使用张三选择读档");
GameMemento memento = caretaker.getGameMemento("张三");
role1.recoverGameMemento(memento);
System.out.println(role1);
}
}
总结
备忘录模式在很多软件的使用过程中普遍存在,但是在应用软件开发中,它的使用频率并不太高,因为现在很多基于窗体和浏览器的应用软件并没有提供撤销操作。如果需要为软件提供撤销功能,备忘录模式无疑是一种很好的解决方案。在一些字处理软件、图像编辑软件、数据库管理系统等软件中备忘录模式都得到了很好的应用。
优点
1、它提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。
2、备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。
缺点
1、资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源。
2、为了节约内存,备忘录模式可以和原型模式配合使用。