1. 设计模式之备忘录模式:将中间数据暂存之后再恢复
1.1. 介绍
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象,备忘录模式属于行为型模式。
备忘录模式允许在不破坏封装性的前提下,捕获和恢复对象的内部状态。
适合场景,当需要提供一种暂存逻辑,可以供撤销恢复等操作。实际项目中,例如各个系统直接通过 MQ 交互,当 MQ 异常时,各个系统通过该模式将数据存储在磁盘中,MQ 恢复后再将磁盘数据发送到 MQ。
备忘录模式 vs 原型模式
对比维度 | 备忘录模式 | 原型模式 |
---|---|---|
目的 | 保存和恢复对象状态。 | 通过克隆创建新对象。 |
状态访问权 | 原发器可访问备忘录内部状态。 | 克隆对象完全独立。 |
典型应用 | 撤销操作、游戏存档。 | 对象复制、性能优化。 |
应用场景
撤销/重做功能:文本编辑器、绘图软件、数据库事务回滚。
游戏存档/读档:保存玩家进度、角色状态。
配置快照:系统配置的备份与恢复。
状态回滚:微服务中的状态补偿(如Saga模式)。
优缺点及建议
优点
1.提供可回溯的状态恢复机制。
2.不破坏封装性,状态安全存储。
3.支持撤销/重做等高级功能。
缺点
1.频繁保存状态可能占用大量内存。
2.深拷贝复杂对象可能影响性能。
使用建议
1.在需要保存和恢复数据状态的场景中使用备忘录模式。
2.考虑使用原型模式结合备忘录模式,以节约内存。
注意事项
1.简单场景:直接保存基本类型或字符串状态。
2.复杂场景:使用序列化实现深拷贝(需确保对象可序列化)。
3.性能敏感场景:结合增量备份(如只保存变化的字段)。
1.2. 实现及相关代码
假设有一个业务,产生中间数据,基于中间数据执行 A 逻辑,然后又基于中间数据执行 B 逻辑。
1.2.1. 相关代码
接口及数据对象
public interface Memento {}
public static class Originator {
private String state;
public void prepare() {
this.state = "中间数据";
}
public void executeA() {
System.out.println("基于中间数据【" + state +"】执行了A方法的逻辑");
// 将state所代表的中间数据做出了修改
state += ",A方法的结果数据";
}
public void executeB() {
System.out.println("基于中间数据【" + state +"】执行了B方法的逻辑");
state += ",B方法的结果数据";
}
public Memento createMemento() {
return new MementoImpl(state);
}
public void setMemento(Memento memento) {
MementoImpl mementoImpl = (MementoImpl)memento;
this.state = mementoImpl.getState();
}
private static class MementoImpl implements Memento {
private String state;
public MementoImpl(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
}
public static class Caretaker {
private Memento memento;
public void saveMemento(Memento memento) {
this.memento = memento;
}
public Memento retrieveMemento() {
return this.memento;
}
}
调用方法
public static void main(String[] args) {
Originator originator = new Originator();
// 准备好了中间数据
originator.prepare();
// 将中间数据保存到备忘录中去
Memento memento = originator.createMemento();
// 将备忘录保存到备忘录管理器中去
Caretaker caretaker = new Caretaker();
caretaker.saveMemento(memento);
// 基于中间数据执行了A方法,但是此时中间数据已经改变了
originator.executeA();
// 从备忘录管理器中获取备忘录
memento = caretaker.retrieveMemento();
// 将备忘录中保存好的中间数据重新设置到原发器中去,就将中间数据恢复成了之前备忘的状态
originator.setMemento(memento);
// 接着再次执行方法B
originator.executeB();
}