设计模式之备忘录模式
备忘录模式又叫做快照模式,属于行为型模式。是指使用一个备忘录对象来存储另一个对象内部状态的快照。备忘录模式的用以是在不破坏封装的条件下,将一个对象的状态捕捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。
开发者对这个模式应该很熟悉,我们使用的开发软件都会有这样的备忘录功能。在编辑时按 Ctrl+Z 组合键时能撤销当前操作,使文档恢复到之前的状态;数据库也有undo、redo的日志记录功能,如果在一个事务内报错了,可以回归到修改之前的数据。
备忘录模式的UML类图如下:
由上图可知备忘录模式主要涉及到备忘录(Memento)角色、发起人(Originator)角色和负责人(Caretaker)角色三个角色:
- 发起人(Originator)角色:创建一个含有当前的内部状态备忘录对象,使用备忘录对象存储其内部状态
- 备忘录(Memento)角色:负责保存好记录,即发起人角色的内部状态。备忘录可以根据发起人对象的判断来决定存储多少发起人对象的内部状态
- 负责人(Caretaker)角色:负责保存备忘录对象,不检查备忘录对象的内容。一般使用记录保存备忘录对象
游戏角色例子
游戏角色有攻击力和防御力,在大战Boss前保存自身的状态(攻击力和防御力),当大战Boss 后攻击力和防御力下降,从备忘录对象恢复到大战前的状态。
例子的UML类图:
备忘录角色:
package com.charon.Memento;
/**
* @className: Memento
* @description: 备忘录角色
* @author: charon
* @create: 2022-04-05 22:43
*/
public class Memento {
private int vit,def;
public Memento(int vit, int def) {
this.vit = vit;
this.def = def;
}
public Memento() {
}
/**
* Gets the value of vit
*
* @return the value of vit
*/
public int getVit() {
return vit;
}
/**
* Gets the value of def
*
* @return the value of def
*/
public int getDef() {
return def;
}
}
负责人角色:
package com.charon.Memento;
import java.util.ArrayList;
import java.util.List;
/**
* @className: Caretaker
* @description: 负责人角色
* @author: charon
* @create: 2022-04-05 22:50
*/
public class Caretaker {
private Memento memento = new Memento();
/**
* Gets the value of mementos
*
* @return the value of mementos
*/
public Memento getMemento() {
return memento;
}
/**
* Sets the mementos
*
* @param memento memento
*/
public void setMemento(Memento memento) {
this.memento = memento;
}
}
发起人角色:
package com.charon.Memento;
/**
* @className: GameRole
* @description:
* @author: charon
* @create: 2022-04-05 23:01
*/
public class GameRole {
private int vit,def;
public GameRole(int vit, int def) {
this.vit = vit;
this.def = def;
}
/**
* Gets the value of vit
*
* @return the value of vit
*/
public int getVit() {
return vit;
}
/**
* Sets the vit
*
* @param vit vit
*/
public void setVit(int vit) {
this.vit = vit;
}
/**
* Gets the value of def
*
* @return the value of def
*/
public int getDef() {
return def;
}
/**
* Sets the def
*
* @param def def
*/
public void setDef(int def) {
this.def = def;
}
/**
* 创建备忘录对象
* @return
*/
public Memento createMemento(){
return new Memento(vit,def);
}
/**
* 从备忘录对象中恢复gameRole的状态
* @param memento
*/
public void recoverGameRoleFromMemento(Memento memento){
this.vit = memento.getVit();
this.def = memento.getDef();
}
public void display(){
System.out.println("当前游戏角色的攻击力为:" + this.vit + " ;防御力为:" + this.def);
}
}
测试:
package com.charon.Memento;
/**
* @className: Client
* @description:
* @author: charon
* @create: 2022-04-04 23:00
*/
public class Client {
public static void main(String[] args) {
// 创建游戏角色
GameRole role = new GameRole(100,100);
System.out.println("大战前的状态:");
role.display();
// 保存当前状态
Caretaker caretaker = new Caretaker();
caretaker.setMemento(role.createMemento());
System.out.println("开始打boss:");
role.setDef(70);
role.setVit(70);
role.display();
System.out.println("大战后,使用备忘录对象恢复到战前");
role.recoverGameRoleFromMemento(caretaker.getMemento());
System.out.println("恢复后的状态:");
role.display();
}
}
打印:
大战前的状态:
当前游戏角色的攻击力为:100 ;防御力为:100
开始打boss:
当前游戏角色的攻击力为:70 ;防御力为:70
大战后,使用备忘录对象恢复到战前
恢复后的状态:
当前游戏角色的攻击力为:100 ;防御力为:100
备忘录模式主要的优点如下:
- 提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。
- 实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
- 简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。
其主要缺点是:
- 资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。
备忘录模式的应用场景
- 需要保存与恢复数据的场景,如玩游戏时的中间结果的存档功能。
- 需要提供一个可回滚操作的场景,如 Word、记事本、Photoshop,Eclipse 等软件在编辑时按 Ctrl+Z 组合键,还有数据库中事务操作。
备忘录模式与命令模式的关系
如果涉及到某个对象的可撤销操作的状态存储问题,那么仅仅使用备忘录模式是不够的,应该考虑使用命令模式。
如果在某个系统中使用命令模式时,需要实现命令的撤销功能,那么命令模式可以使用备忘录模式来存储可撤销操作的状态。
本文版权归Charon和博客园共有,原创文章,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。