在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
备忘录模式主要是备份对象某一时间的状态,以供后续可能恢复使用。因此有以下几个角色来实现这一功能:发起人角色 Originator、备忘录角色 Memento 和备忘录管理员角色 Caretaker。
发起人角色就是要备份的对象,它负责定义要备份的范围,还要负责创建和恢复备忘录数据,其具体实现如下:
package com.yrs.memento.standard;
/**
* @Author: yangrusheng
* @Description: 发起人角色
* @Date: Created in 17:28 2020/6/21
* @Modified By:
*/
public class Originator {
/**
* 内部状态
*/
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
/**
* 创建一个备忘录
* @return
*/
public Memento createMemento() {
return new Memento(this.state);
}
/**
* 恢复备数据
* @param memento
*/
public void restoreMemento(Memento memento) {
this.setState(memento.getState());
}
}
备忘录角色的责任就是要存储备份对象的数据,以便在恢复数据时提供数据。其具体实现如下:
package com.yrs.memento.standard;
/**
* @Author: yangrusheng
* @Description: 备忘录角色
* @Date: Created in 17:35 2020/6/21
* @Modified By:
*/
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;
}
}
备忘录管理角色的作用是管理备忘录,提供保存和获取备忘录等功能。简单场景下可能看不出太大作用,但是在复杂场景下,上层模块使用备忘录时就会变得麻烦起来,因此通过一个管理类更容易使用。其具体实现如下:
package com.yrs.memento.standard;
/**
* @Author: yangrusheng
* @Description: 备忘录管理员角色
* @Date: Created in 17:36 2020/6/21
* @Modified By:
*/
public class Caretaker {
private Memento memento;
public Memento getMemento() {
return memento;
}
public void setMemento(Memento memento) {
this.memento = memento;
}
}
下面看下场景类如何使用备忘录:
package com.yrs.memento.standard;
/**
* @Author: yangrusheng
* @Description:
* @Date: Created in 18:16 2020/6/21
* @Modified By:
*/
public class Client {
public static void main(String[] args) {
Originator originator = new Originator();
originator.setState("state1");
System.out.println("init state: " + originator.getState());
// 备份
Caretaker caretaker = new Caretaker();
caretaker.setMemento(originator.createMemento());
// 改变状态
originator.setState("state2");
System.out.println("changed state: " + originator.getState());
// 恢复之前状态
originator.restoreMemento(caretaker.getMemento());
System.out.println("restore state: " + originator.getState());
}
}
发起人角色实现 Cloneable 接口,把备忘录角色和管理者角色都集成到发起人内部,这样就大大简化了备忘录模式的使用。
package com.yrs.memento.clone;
import com.yrs.memento.standard.Memento;
/**
* @Author: yangrusheng
* @Description: 发起人角色
* @Date: Created in 17:28 2020/6/21
* @Modified By:
*/
public class Originator implements Cloneable {
private Originator backup;
/**
* 内部状态
*/
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
/**
* 创建一个备忘录
*/
public void createMemento() {
this.backup = this.clone();
}
/**
* 恢复备数据
*/
public void restoreMemento() {
if (this.backup != null) {
this.setState(this.backup.state);
}
}
/**
* clone 对象
* @return
*/
@Override
protected Originator clone() {
try {
return (Originator) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
当发起人角色有多个状态时,备忘录角色可以使用 HashMap 来存储多个状态,备份和恢复时需要遍历对象的成员变量来一一取值赋值,这里需要一个工具类来做成员变量的取值和赋值,代码具体实现见:
https://github.com/ByrsH/Design-Patterns/tree/master/Design%20Patterns/src/main/java/com/yrs/memento/moreState, 类图如下:
备忘录管理员使用 HashMap 来保存不同版本的备份,保存和读取都有根据版本来操作。详细代码见:https://github.com/ByrsH/Design-Patterns/tree/master/Design%20Patterns/src/main/java/com/yrs/memento/moreBackups
package com.yrs.memento.moreBackups;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: yangrusheng
* @Description: 备忘录管理员角色
* @Date: Created in 17:36 2020/6/21
* @Modified By:
*/
public class Caretaker {
private Map<String, Memento> mementoMap = new HashMap<>();
public Memento getMemento(String key) {
return mementoMap.get(key);
}
public void setMemento(String key, Memento memento) {
mementoMap.put(key, memento);
}
}
备份数据是不能被修改的,为了保证其安全性,我们定义一个空备忘录接口,然后在发起人角色内部定义内部类备忘录实现该接口。这样就能保证只有发起人可以修改数据。具体实现见:https://github.com/ByrsH/Design-Patterns/tree/master/Design%20Patterns/src/main/java/com/yrs/memento/security
《设计模式之禅-第2版》