引言
上一节我们说了享元模式,这节本来打算说访问者模式的,但是太抽象,而且用的不多,所以本章节讲备忘录模式。
示例地址
类图
image定义
在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样,以后就可以将该对象恢复到原先保存的状态。
使用场景
1. 需要保存一个对象在某一个时刻的状态或部分状态。
2. 如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问其内部状态,通过中间对象可以间接访问其内部状态。
备忘录模式角色
Originator:负责创建一个备忘录,可以记录、恢复自身的内部状态。同时Originator还可以根据需要决定Memento存储自身的那些内部状态。
Memento:备忘录角色,用于存储Originator的内部状态,并且可以防止Originator以外的对象访问Memento。
Caretaker:负责存储备忘录,不能对备忘录的内容进行操作和访问,只能够将备忘录传递给其他对象。
备忘录模式中的相关概念
1. 窄接口:
只允许它把备忘录对象传给其他的对象。针对的是负责人对象和其他除发起人对象之外的任何对象。
2. 宽接口:
允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态。针对发起人。
备忘录模式中的四种模式
-
白箱模式
将发起人角色的状态存储在一个大家都看得到的地方,备忘录角色的内部所存储的状态就对所有对象公开。因此这个实现又叫做“白箱实现”。
1. 定义备忘录
/**
* 定义备忘录
*
* @author 512573717@qq.com
* @created 2018/8/9 上午11:23.
*/
public class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
2. 创建发起人角色
/**
* 创建发起人角色
*
* @author 512573717@qq.com
* @created 2018/8/9 上午10:59.
*/
public class Originator {
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
System.out.println("当前状态:" + this.state);
}
/**
* 工厂方法,返回一个新的备忘录对象
*
* @return
*/
public Memento createMemento() {
return new Memento(state);
}
/**
* 将发起人的状态恢复到备忘录对象所记录的状态
*
* @param memento
*/
public void restoreMemento(Memento memento) {
this.state = memento.getState();
}
@Override
public String toString() {
return "Originator{" +
"state='" + state + '\'' +
'}';
}
}
3. 定义负责人角色
/**
* 定义负责人角色
*
* @author 512573717@qq.com
* @created 2018/8/9 上午11:28.
*/
public class Caretaker {
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
4. Client
Originator originator = new Originator();
originator.setState("state1");
System.out.println(originator);
Caretaker caretaker = new Caretaker();
caretaker.setMemento(originator.createMemento());
originator.setState("state2");
System.out.println(originator);
originator.restoreMemento(caretaker.getMemento());
System.out.println(originator);
-
黑箱模式
备忘录角色对发起人(Originator)角色对象提供一个宽接口,而为其他对象提供一个窄接口。这样的实现叫做“黑箱实现”。 简单说就是备忘录是一个接口,实现类放到发起人里面去了,外界看不见。
1. 定义接口
/**
* 定义备忘录接口
*
* @author 512573717@qq.com
* @created 2018/8/10 上午10:18.
*/
public interface MementoIF {
}
2. 创建发起人角色
/**
* 创建发起人角色
*
* @author 512573717@qq.com
* @created 2018/8/10 下午2:04.
*
*/
public class Originator {
public String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
System.out.println("赋值状态:" + state);
}
/**
* 工厂方法,返还一个新的备忘录对象
*/
public Memento createMemento() {
return new Memento(this);
}
/**
* 将发起人恢复到备忘录对象所记录的状态上
*/
public void restoreMemento(MementoIF memento) {
this.state = ((Memento) memento).getState();
}
@Override
public String toString() {
return "Originator{" +
"state='" + state + '\'' +
'}';
}
private class Memento implements MementoIF {
private String state;
/**
* 构造方法
*/
private Memento(Originator o) {
this.state = o.state;
}
private String getState() {
return state;
}
}
}
3. 定义负责人角色
/**
* 定义负责人角色
*
* @author 512573717@qq.com
* @created 2018/8/10 下午2:04.
*/
public class Caretaker {
private MementoIF memento;
/**
* 备忘录取值方法
*/
public MementoIF retrieveMemento() {
return memento;
}
/**
* 备忘录赋值方法
*/
public void saveMemento(MementoIF memento) {
this.memento = memento;
}
}
4. Client
Originator o = new Originator();
Caretaker c = new Caretaker();
//改变负责人对象的状态
o.setState("state1");
System.out.println("保存中"+o);
//创建备忘录对象,并将发起人对象的状态存储起来
c.saveMemento(o.createMemento());
//修改发起人对象的状态
o.setState("state2");
System.out.println("设置新的值"+o);
//恢复发起人对象的状态
o.restoreMemento(c.retrieveMemento());
System.out.println("恢复上次的值"+o);
-
多重检查点
常见的系统往往需要存储不止一个状态,而是需要存储多个状态,或者叫做有多个检查点。用list或者map存储,这种叫做多重检查点
1. 定义备忘录
/**
* 定义备忘录
*
* @author 512573717@qq.com
* @created 2018/8/9 上午11:23.
*/
public class Memento {
private List<String> states;
/**
* 构造函数
*/
public Memento(List<String> states) {
//每次存放新的
this.states = new ArrayList<String>(states);
}
public List<String> getStates() {
return states;
}
}
2. 创建发起人角色
/**
* 创建发起人角色
*
* @author 512573717@qq.com
* @created 2018/8/10 下午2:04.
*
*/
public class Originator {
private List<String> states;
/**
* 构造函数
*/
public Originator(){
states = new ArrayList<String>();
}
/**
* 工厂方法,返还一个新的备忘录对象
*/
public Memento createMemento(){
return new Memento(states );
}
/**
* 将发起人恢复到备忘录对象记录的状态上
*/
public void restoreMemento(Memento memento){
states = memento.getStates();
}
/**
* 状态的赋值方法
*/
public void setState(String state){
states.add(state);
}
/**
* 辅助方法,打印所有状态
*/
public void printStates(){
for(String state : states){
System.out.println(state);
}
}
}
3. 定义负责人角色
/**
* 定义负责人角色
*
* @author 512573717@qq.com
* @created 2018/8/9 上午11:28.
*/
public class Caretaker {
private Originator o;
private List<Memento> mementos = new ArrayList<Memento>();
/**
* 构造函数
*/
public Caretaker(Originator o){
this.o = o;
}
/**
* 创建一个新的检查点
*/
public void createMemento(){
Memento memento = o.createMemento();
mementos.add(memento);
}
/**
* 将发起人恢复到某个检查点
*/
public void restoreMemento(int index){
Memento memento = mementos.get(index);
o.restoreMemento(memento);
}
/**
* 将某个检查点删除
*/
public void removeMemento(int index){
mementos.remove(index);
}
}
4. Client
//多重点检查
Originator o = new Originator();
Caretaker c = new Caretaker(o);
//改变状态
o.setState("state 0");
//建立一个检查点
c.createMemento();
//改变状态
o.setState("state 1");
//建立一个检查点
c.createMemento();
//改变状态
o.setState("state 2");
//建立一个检查点
c.createMemento();
//改变状态
o.setState("state 3");
//建立一个检查点
c.createMemento();
//改变状态
o.setState("state 4");
//建立一个检查点
c.createMemento();
//打印出所有检查点
o.printStates();
System.out.println("-----------------恢复检查点-----------------");
//恢复到第二个检查点
c.restoreMemento(2);
//打印出所有检查点
o.printStates();
-
自述历史模式
发起人角色自己兼任负责人角色。
1. 定义备忘录接口
/**
* 定义备忘录接口
*
* @author 512573717@qq.com
* @created 2018/8/10 上午10:18.
*/
public interface MementoIF {
}
2. 创建发起人角色
/**
* 创建发起人角色
*
* @author 512573717@qq.com
* @created 2018/8/10 下午2:04.
*/
public class Originator {
public String state;
/**
* 改变状态
*/
public void setState(String state) {
this.state = state;
}
/**
* 工厂方法,返还一个新的备忘录对象
*/
public Memento createMemento() {
return new Memento(this);
}
/**
* 将发起人恢复到备忘录对象所记录的状态上
*/
public void restoreMemento(MementoIF memento) {
Memento m = (Memento) memento;
setState(m.state);
}
@Override
public String toString() {
return "Originator{" +
"state='" + state + '\'' +
'}';
}
private class Memento implements MementoIF {
private String state;
/**
* 构造方法
*/
private Memento(Originator o) {
this.state = o.state;
}
private String getState() {
return state;
}
}
}
3. Client
Originator o = new Originator();
//修改状态
o.setState("state 0");
//创建备忘录
MementoIF memento = o.createMemento();
//修改状态
o.setState("state 1");
//按照备忘录恢复对象的状态
o.restoreMemento(memento);
备忘录模式示例
最近腾讯有一款很火的游戏,叫做王者荣耀,当我们正在打游戏的时候,突然来电话了,这个时候怎么处理。来看看我们的备忘录模式怎么实现。
1. 发起人角色(王者荣耀)
/**
* 王者荣耀 游戏
*
* @author 512573717@qq.com
* @created 2018/8/9 上午11:53.
*/
public class WangZheRongYao {
//开始的总时长
private int time;
// 当前的等级
private int level;
// 是否退出
private boolean isExit = false;
/**
* 开始一把排位
*/
public void playGame() {
new Thread(new Runnable() {
@Override
public void run() {
while (!isExit) {
System.out.println("游戏开始了:" + time + "分钟,等级:" + level);
time++;
level++;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
/**
* 来电话了,退出当前游戏
*/
public void exitGame() {
isExit = true;
System.out.println("=====来电话了,退出当前游戏=====");
System.out.println("游戏开始了:" + time + "分钟,等级:" + level);
}
/**
* 保存获取当前游戏信息
*
* @return
*/
public GameInfo saveGameInfo() {
return new GameInfo(time, level);
}
/**
* 重新加载游戏
*
* @param gameInfo
*/
public void resetGame(GameInfo gameInfo) {
time = gameInfo.getTime();
level = gameInfo.getLevel();
System.out.println("=====恢复游戏=====");
System.out.println("游戏开始了:" + time + "分钟,等级:" + level);
// isExit = false;
}
}
2. 备忘录
/**
* 创建备忘录
*
* @author 512573717@qq.com
* @created 2018/8/9 下午1:57.
*/
public class GameInfo {
//开始的总时长
private int time;
// 当前的等级
private int level;
public GameInfo(int time, int level) {
this.time = time;
this.level = level;
}
public int getTime() {
return time;
}
public void setTime(int time) {
this.time = time;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
}
3. 负责人角色
/**
* 备忘录管理者
*
* @author 512573717@qq.com
* @created 2018/8/9 下午1:59.
*/
public class GameManager {
private GameInfo mGameInfo;
private static volatile GameManager instance;
private GameManager() {
}
public static GameManager getGameManager() {
if (instance == null) {
synchronized (GameManager.class) {
if (instance == null) {
instance = new GameManager();
}
}
}
return instance;
}
/**
* 保存游戏信息
*
* @param gameInfo
*/
public void saveGameInfo(GameInfo gameInfo) {
mGameInfo = gameInfo;
}
/**
* 读取游戏信息
*
* @return
*/
public GameInfo getGameInfo() {
return mGameInfo;
}
}
4. Client
WangZheRongYao wzry = new WangZheRongYao();
wzry.playGame();
try {
//玩了一会
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//暂停游戏
wzry.exitGame();
GameManager.getGameManager().saveGameInfo(wzry.saveGameInfo());
//恢复游戏
wzry.resetGame(GameManager.getGameManager().getGameInfo());
总结
优点
1. 它提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。
2. 备忘录实现了对信息的封装,一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。
缺点
资源消耗过大,如果需要保存的原发器类的成员变量太多,就不可避免需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源。
网友评论