十七、设计模式之备忘录模式
所属类型 | 定义 |
---|---|
行为型 | 保存一个对象的某个状态,以便在适当的时候恢复对象 |
能帮我们干什么?
主要解决什么问题?
主要解决的是 在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态
何时使用:很多时候我们总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态,使得他有"后悔药"可吃。
如何解决:通过一个备忘录类专门存储对象状态。
优缺点
优点
1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。
2、实现了信息的封装,使得用户不需要关心状态的保存细节。
缺点:
注意事项:
1、为了符合迪米特原则,还要增加一个管理备忘录的类。
2、为了节约内存,可使用原型模式+备忘录模式。
使用的场景
1、为了符合迪米特原则,还要增加一个管理备忘录的类。 2、为了节约内存,可使用原型模式+备忘录模式。
实现
关键代码:客户不与备忘录类耦合,与备忘录管理类耦合。
角色
-
Originator(发起人):这是需要被记录状态的对象。它创建一个备忘录对象,以存储当前状态,也可以从备忘录中恢复状态。
-
Memento(备忘录):备忘录对象用于存储Originator的状态。通常,备忘录对象具有与原始对象相同的接口,但不会直接暴露其内部状态。
-
Caretaker(负责人):负责管理备忘录对象。它可以存储多个备忘录对象,以便在需要时进行状态恢复。
备忘录模式
备忘录模式能帮我们在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
难度: ⭐️ ⭐️
举例:
我们都玩过单机游戏吧。其中单机游戏的存档机制就是我们要讨论的备忘录模式。
一个游戏,当我们打怪,升级,爆装备的时候,发现一个极品装备,或者通关了一个及其困难的关卡,我们肯定会赶紧存档。不然游戏死亡或意外退出。我们可就GG了。
- 游戏玩家数据,就是Originator。玩家的实时游戏数据。
- 游戏历史存档 就是Memento。记录游戏玩家数据
- 游戏历史档案管理器,就是Caretaker.包括存档,和加载操作
package com.kongxiang.raindrop.dp.type.behavior.memento;
import lombok.Getter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
/**
* 主机游戏(Originator)
*
* @author 孔翔
* @since 2023-10-20
* copyright for author : 孔翔 at 2023-10-20
* dp
*/
@Getter
public class HostGame {
/**
* 人物登记
*/
private int level;
/**
* 装备
*/
private List<String> equipment;
/**
* 开始游戏 新开始
*/
public void newPlay() {
this.level = 0;
this.equipment = new ArrayList<>();
}
/*
*打怪
*/
public String attack() {
// 升级
level++;
return UUID.randomUUID().toString().substring(20,34)+ ":装备衣服 : level" + (level + 10 - (level % 10) + " 级 - " + ((int)(Math.random()*10 %3)+1) +"⭐品质" );
}
/**
* 拾取
*/
public void pickup(String equipment) throws Exception {
if (this.equipment.size() >= 30) {
throw new Exception("装备背包不足,请购买");
}
this.equipment.add(equipment);
}
/**
* 保存备份
*/
public GameMemento saveGame() {
List<String> backup = new LinkedList<>();
backup.addAll(this.equipment);
return new GameMemento(this.level, backup);
}
/**
* 加载之前的游戏备份
*/
public void loadGame(GameMemento gameMemento) {
this.level = gameMemento.getLevel();
this.equipment = gameMemento.getEquipment();
}
}
Memento
package com.kongxiang.raindrop.dp.type.behavior.memento;
import lombok.ToString;
import java.util.List;
/**
* 游戏存档(Memento)
*
* @author 孔翔
* @since 2023-10-20
* copyright for author : 孔翔 at 2023-10-20
* dp
*/
@ToString
public class GameMemento {
private int level;
private List<String> equipment;
public GameMemento(int level, List<String> equipment) {
this.level = level;
this.equipment = equipment;
}
public int getLevel() {
return level;
}
public List<String> getEquipment() {
return equipment;
}
}
CareTaker
package com.kongxiang.raindrop.dp.type.behavior.memento;
import java.util.LinkedList;
import java.util.List;
/**
* 历史备份(CareTaker)
* 管理备份
*
* @author 孔翔
* @since 2023-10-20
* copyright for author : 孔翔 at 2023-10-20
* dp
*/
public class HistoryMemento {
private static final List<GameMemento> history = new LinkedList<>();
/**
* 保存存档(备忘录)
*/
public void addHistory(GameMemento game) {
history.add(game);
}
/**
* 选择指定的存档(备忘录)
*/
public GameMemento loadHistory(int index) {
return history.get(index);
}
public void list() {
history.forEach(System.out::println);
}
}
client
package com.kongxiang.raindrop.dp.type.behavior.memento;
/**
* @author 孔翔
* @since 2023-10-20
* copyright for author : 孔翔 at 2023-10-20
* dp
*/
public class HostGamePlayerMain {
public static void main(String[] args) throws Exception {
HostGame player = new HostGame();
HistoryMemento history = new HistoryMemento();
// 新游戏刚入手,开始体验
player.newPlay();
// 打怪,升级,爆装备
String eq = player.attack();
// 拾取装备
player.pickup(eq);
eq = player.attack();
player.pickup(eq);
System.out.println("高等级极品装备");
// 备份
GameMemento gameMemento = player.saveGame();
history.addHistory(gameMemento);
for (int i = 0; i < 23; i++) {
System.out.println("打怪升级捡装备");
eq = player.attack();
player.pickup(eq);
// 每五次备份一次
if (i % 5 == 0) {
// 备份
gameMemento = player.saveGame();
history.addHistory(gameMemento);
}
}
System.out.println("---------- 当期的玩家数据 ------------");
System.out.println(" level:" + player.getLevel() );
System.out.println(" 装备列表 :" );
player.getEquipment().forEach(System.out::println);
System.out.println("-----------------------------------");
System.out.println("查看备份列表");
history.list();
System.out.println("回退到第3个存档,重新打boss");
GameMemento gameMemento1 = history.loadHistory(3-1);
player.loadGame(gameMemento1);
System.out.println("---------- 当期的玩家数据 ------------");
System.out.println(" level:" + player.getLevel() );
System.out.println(" 装备列表 :" );
player.getEquipment().forEach(System.out::println);
System.out.println("-----------------------------------");
System.out.println("查看备份列表");
history.list();
}
}
总结
备忘录模式能帮我们在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
备忘录模式使得对象的状态管理更加灵活。它允许对象在不暴露其内部结构的情况下进行状态的保存和恢复。这有助于实现撤销和重做功能,以及历史记录和快照功能。然而,使用备忘录模式可能会增加一些内存开销,特别是如果需要存储大量的状态历史。
总之,备忘录模式在需要记录和恢复对象状态的情况下是一个有用的设计模式。它可以帮助保持代码的清晰性和可维护性,同时提供强大的状态管理功能。