还是把这张概总图放这里。
备忘录模式,也叫快照(Snapshot)模式,英文翻译是Memento Design Pattern
。在 GoF 的《设计模式》一书中,备忘录模式是这么定义的:
Captures and externalizes an object’s internal state so that it can be restored later, all without violating encapsulation.
翻译成中文就是:在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态。
概念性的东西,表述得很正确,但如果不懂的,依旧不明白它在说什么。
举个例子给大家分享下:
游戏中的存档功能就是备忘录模式。我们在玩游戏时,到一定的进度时,我们觉得当前的状态就是最有利的,因此我们会调用存档的功能,把当前的状态存储到某个地方或者某个文件中。当我们后续的某个操作出现问题时,再读取存档就能回到我们刚才保存的进度。
就刚才的例子。我们把整个游戏看做一个对象:Game。在这个游戏中,我们的进度,就是描述的当前游戏的状态,比如我们赚取的金布数量。因此,在 Game 中始终有一个金币数量的状态:money。
现在,我们想要把这个金币的数量进行存档。在游戏中,是不是经常有一个存档的功能?这就对应着:Game中有一个存档的功能方法。同样,我们进行读档的时候,对应着的就是 Game 中读档的功能方法。
存档或读档的数据也是游戏的一种状态。我们不会把这个存档的状态和 Game 中现有的状态混在一起。因为在读档的时候,存档中的状态会覆盖 Game 中原有的状态。因此,我们需要一个独立的对象来表示这个存档状态(为什么用对象呢?因为我们的状态可能不止金币,可能还有装备,或者其他的):Memento。
目前,我们已经提到了备忘录中的两个角色了:发起者角色(Game)和备忘录角色(Memento
)。
注意,这里还有一个角色:备忘录管理者角色
。它用来管理备忘录,仅提供备忘录的存储和获取。为什么呢?因为你的备忘录可能不止一个,就像你的存档不止一个一样。
🆗,让我们简单的实现下这个例子。
首先时发起者角色:Game 类。
public class Gamer {
private int money;
public int getMoney(){
return money;
}
public Gamer(int money){
this.money=money;
}
public Memento createMemento(){
Memento m=new Memento(money);
Iterator it=fruits.iterator();
while(it.hasNext()){
String fruit=(String)it.next();
if(fruit.startsWith("好吃的")){
m.addFruits(fruit);
}
}
return m;
}
public void restoreMemento(Memento memento){
this.money=memento.getMoney();
this.fruits=memento.getFruits();
}
public String toString(){
return "Money:"+money+" ,Fruits:"+fruits;
}
}
接着,增加存档和读档的方法:createMemento() 和 restoreMemento()
public class Gamer {
private int money;
public void setMoney(int money) {
this.money = money;
}
public int getMoney(){
return money;
}
public Gamer(){
}
public Gamer(int money){
this.money=money;
}
public Memento createMemento(){
Memento m=new Memento(money);
return m;
}
public void restoreMemento(Memento memento){
this.money=memento.getMoney();
}
public String toString(){
return "Money:"+money;
}
}
在存取档中,用到了备忘录对象:Memento
public class Memento {
private int money;
Memento(int money){
this.money=money;
}
public int getMoney(){
return money;
}
}
注意:备忘录中没有setter方法。为什么呢?大家想一想!*
还有一个类就是备忘录管理者类:MementoManager.
public class MementoManager {
private List<Memento> mementoList = new ArrayList<>();
public void add(Memento memento) {
mementoList.add(memento);
}
public Memento get(int index) {
return mementoList.get(index);
}
}
让我们来测试一把。
public class GameTest {
public static void main(String[] args) {
Game game = new Game();
MementoManager mementoManager = new MementoManager();
game.setMoney(100);
System.out.println("当前的金币是:" + game.getMoney());
mementoManager.add(game.createMemento());
// 不小心金币损失到只有10了!
game.setMoney(10);
System.out.println("当前的金币是:" + game.getMoney());
//从MementoManager取出第一个Memento对象,并给Game赋值回去。(简称:读档)
game.restoreMemento(mementoManager.get(0));
System.out.println("执行读档后,当前的金币是:" + game.getMoney());
}
}
看下结果:当前的金币是:100当前的金币是:10。执行读档后,当前的金币是:100。
我们的金币数量,通过读档后,又回到了起初的100。
这就是备忘录的使用。
备忘录模式是一种简单而有效的数据保存与恢复方法。它通过发起者和备忘录之间的协作,实现了对象状态的临时保存与恢复。
优点:
缺点: 如果备忘录对象特别多,那你的备忘录管理者就要存储下所有的对象。这就会消耗大量的内存资源。
因此,在实践中,备忘录管理者都会限制备忘录的数量。也就是我们游戏中,存档的位置是有限制的,有的可以存16个存档,有的可以存32个存档。
备忘录模式在实践中的其他用途
大家还想到了哪些应用场景呢?欢迎留言!