前言:
备忘录模式(Memento Pattern)在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将对象恢复到原先保存的状态。
一.备忘录模式简介
备忘录模式(Memento Pattern)适用于功能比较复杂,但需要维护和记录的历史类,或者需要保存属性只是众多属性中的一小部分时,用备忘录模式可以将信息还原。
二.备忘录模式的实例讲解
案例:游戏主角在战斗中死亡后复活,可以通过备忘录模式实现。
1.产品设计UML类图
(图片加载慢,多刷新几下,耐心等待……)
注:管理者类Caretaker
和备忘录类Memento
是聚合关系,Caretaker
包含Memento
,但不是Memento
的一部分。
聚合关系:表示一种弱的"拥有"关系,体现的是A对象可以包含B对象,但B对象不是A对象的一部分。
2.游戏角色类(发起人)
设计分析:创建一个备忘录类(Memento
),实现保存游戏角色的状态,并且可以恢复游戏角色的状态。可以根据需求,有选择的决定保存那些状态,这里只保存游戏角色的生命值hp
、攻击力atk
、防御力def
。
package com.patttern.originator;
import com.patttern.memento.Memento;
/**
* 创建一个备忘录,储存状态,用于还原
*/
public class Originator {
// 生命值
private int hp;
// 攻击力
private int atk;
// 防御力
private int def;
// 状态
private String state;
// 角色姓名
private String name;
//显示当前状态
public void showState() {
System.out.println(getName() + "当前状态:生命值:" + getHp() + ",攻击力:" + getAtk() + ",防御力" + getDef());
}
//初始化角色状态
public void getInitState(String name) {
setName(name);
setHp(1000);
setAtk(80);
setDef(50);
}
//被击杀,状态清零
public void killed() {
setHp(0);
setAtk(0);
setDef(0);
}
//创建备忘录对象
public Memento CreateMemento() {
return new Memento();
}
//保存角色状态到备忘录中
public Memento saveState() {
return new Memento(hp, atk, def);
}
//恢复数据
public void recoveryState(Memento memento) {
this.hp = memento.getHp();
this.atk = memento.getAtk();
this.def = memento.getDef();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public int getHp() {
return hp;
}
public void setHp(int hp) {
this.hp = hp;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
}
3.备忘录类
设计分析: 储存游戏角色的状态,并禁止该游戏角色以外的其他对象对备忘录的访问。备忘录有两个接口,一个宽接口对游戏角色(Originator
)开放,使游戏角色可以恢复状态数据;另一个窄接口,对管理者(Caretaker
)公开,它只能将备忘录传递给其他对象。
ppackage com.patttern.memento;
/**
* 备忘录:用于保存状态数据
*
*/
public class Memento {
//生命值
private int hp;
//攻击力
private int atk;
//防御力
private int def;
public Memento() {
}
public Memento(int hp, int atk, int def) {
this.hp = hp;
this.atk = atk;
this.def = def;
}
public int getHp() {
return hp;
}
public void setHp(int hp) {
this.hp = hp;
}
public int getAtk() {
return atk;
}
public void setAtk(int atk) {
this.atk = atk;
}
public int getDef() {
return def;
}
public void setDef(int def) {
this.def = def;
}
}
4.管理者类(Caretaker)
设计分析: 维护一个备忘录Memento
对象的具体实例,只能管理备忘录,但不能对其内容操作和检查。
为什么不直接在客户端中维护备忘录Memento
,而非要在Caretaker
中维护呢?这是因为开发中要符合迪米特法则,让客户端和备忘录Memento
不直接通信,而在管理者的帮助下完成通信。
迪米特法则:如果两个类不直接通信,那么这两个类就不应当发生直接的相互作用。如果其中一个类要调用另一个类中某个方法的话,可通过第三者转发这个调用。
代码如下:
package com.patttern.caretaker;
import com.patttern.memento.Memento;
/**
* 管理角色状态
*
*/
public class Caretaker {
//管理备忘录对象
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
5.客户端中使用
模拟英雄盖伦的生命周期,创建人物-备份状态-战斗死亡-复活恢复状态。
package com.patttern.client;
import com.patttern.caretaker.Caretaker;
import com.patttern.originator.Originator;
public class Client {
public static void main(String[] args) {
//初始化
Originator garen = new Originator();
garen.getInitState("盖伦");
garen.showState();
//备份状态
Caretaker caretaker = new Caretaker();
caretaker.setMemento(garen.saveState());
//在战斗中被杀
garen.killed();
garen.showState();
//复活-恢复状态
garen.recoveryState(caretaker.getMemento());
garen.showState();
}
}
6.运行结果展示
盖伦当前状态:生命值:1000,攻击力:80,防御力50
盖伦当前状态:生命值:0,攻击力:0,防御力0
盖伦当前状态:生命值:1000,攻击力:80,防御力50
7.源码下载
本文示例代码下载地址:点击下载
三.总结:
备忘录模式适合应用于复杂的,但需要维护或记录属性历史的类,或者需要保存的属性只是众多属性的一部分时,Originator
类可以根据备忘录中的信息还原到以前状态。
1.备忘录模式的优点
- 方便用户恢复到某个历史状态,如撤销功能。
- 实现了对状态信息的封装,用户不需要关心保存细节。
2.备忘录模式的缺点
- 若果状态成员变量过多,每次保存都会消耗很多内存资源。