备忘录模式详解
目录
模式简介
定义
备忘录模式(Memento Pattern)是一种行为型设计模式,它允许在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后可以将该对象恢复到原先保存的状态。
核心思想
- 状态保存:在不破坏对象封装性的前提下,保存对象的内部状态
- 状态恢复:在需要时能够将对象恢复到之前保存的状态
- 历史管理:提供管理多个历史状态的能力
模式结构
- Originator(发起人):需要保存状态的对象
- Memento(备忘录):存储发起人内部状态的对象
- Caretaker(管理者):负责保存和恢复备忘录的对象
核心流程
基本流程图
详细流程步骤
1. 状态保存流程
2. 状态恢复流程
代码示例流程
// 1. 创建原始对象
Originator originator = new Originator();
originator.setState("State1");
// 2. 保存状态
Memento memento = originator.createMemento();
Caretaker caretaker = new Caretaker();
caretaker.setMemento(memento);
// 3. 改变状态
originator.setState("State2");
// 4. 恢复状态
originator.restoreMemento(caretaker.getMemento());
重难点分析
重点分析
1. 封装性保护
public class Originator {
private String state;
// 只有Originator能创建和访问Memento的内部状态
public Memento createMemento() {
return new Memento(this.state);
}
public void restoreMemento(Memento memento) {
this.state = memento.getState();
}
}
// Memento类设计为包私有或内部类
class Memento {
private final String state;
Memento(String state) {
this.state = state;
}
String getState() {
return state;
}
}
2. 状态管理策略
public class Caretaker {
private List<Memento> mementos = new ArrayList<>();
private int currentIndex = -1;
// 保存状态
public void saveMemento(Memento memento) {
// 删除当前位置之后的所有状态
mementos = mementos.subList(0, currentIndex + 1);
mementos.add(memento);
currentIndex++;
}
// 撤销
public Memento undo() {
if (currentIndex > 0) {
currentIndex--;
return mementos.get(currentIndex);
}
return null;
}
// 重做
public Memento redo() {
if (currentIndex < mementos.size() - 1) {
currentIndex++;
return mementos.get(currentIndex);
}
return null;
}
}
难点分析
1. 内存管理
public class Caretaker {
private static final int MAX_MEMENTOS = 50;
private List<Memento> mementos = new ArrayList<>();
public void saveMemento(Memento memento) {
mementos.add(memento);
// 限制历史记录数量,防止内存溢出
if (mementos.size() > MAX_MEMENTOS) {
mementos.remove(0);
}
}
}
2. 深拷贝与浅拷贝
public class Originator {
private List<String> stateList;
public Memento createMemento() {
// 深拷贝,避免状态被意外修改
List<String> copyList = new ArrayList<>(stateList);
return new Memento(copyList);
}
public void restoreMemento(Memento memento) {
// 深拷贝恢复
this.stateList = new ArrayList<>(memento.getStateList());
}
}
3. 并发安全
public class Caretaker {
private final List<Memento> mementos = Collections.synchronizedList(new ArrayList<>());
private volatile int currentIndex = -1;
public synchronized void saveMemento(Memento memento) {
mementos.add(memento);
currentIndex++;
}
public synchronized Memento undo() {
if (currentIndex > 0) {
currentIndex--;
return mementos.get(currentIndex);
}
return null;
}
}
Spring中的源码分析
1. 事务回滚机制
核心类:TransactionSynchronizationManager
public abstract class TransactionSynchronizationManager {
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");
// 注册同步回调,类似备忘录模式的状态保存
public static void registerSynchronization(TransactionSynchronization synchronization) {
Set<TransactionSynchronization> synchs = synchronizations.get();
if (synchs == null) {
synchs = new LinkedHashSet<>();
synchronizations.set(synchs);
}
synchs.add(synchronization);
}
}
事务状态保存与恢复
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager {
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
// 保存当前状态(类似Memento)
if (txObject.getConnectionHolder() == null) {
Connection newCon = obtainDataSource().getConnection();
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
// 设置事务隔离级别
prepareTransactionalConnection(con, definition);
} catch (Throwable ex) {
// 状态恢复(类似restoreMemento)
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, getDataSource());
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
}
2. 配置属性管理
核心类:ConfigurationPropertiesBindingPostProcessor
public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
// 保存原始配置状态
ConfigurationProperties annotation = AnnotationUtils.findAnnotation(
bean.getClass(), ConfigurationProperties.class);
if (annotation != null) {
// 创建配置备忘录
ConfigurationPropertiesState state = createConfigurationState(bean);
// 绑定新配置
bindConfigurationProperties(bean, state);
}
return bean;
}
// 配置状态保存(备忘录模式)
private ConfigurationPropertiesState createConfigurationState(Object bean) {
return new ConfigurationPropertiesState(bean);
}
}
3. 会话管理
核心类:HttpSession
public class StandardSession implements HttpSession, HttpSessionActivationListener {
// 会话状态保存
public void setAttribute(String name, Object value) {
// 保存属性到会话中
if (value == null) {
removeAttribute(name);
} else {
attributes.put(name, value);
// 标记会话为已修改(状态变更)
if (isValid) {
manager.touch(this);
}
}
}
// 会话状态恢复
public Object getAttribute(String name) {
if (!isValid) {
throw new IllegalStateException("Session has been invalidated");
}
return attributes.get(name);
}
}
4. 缓存状态管理
核心类:ConcurrentMapCache
public class ConcurrentMapCache extends AbstractValueAdaptingCache {
private final ConcurrentMap<Object, Object> store;
@Override
protected Object lookup(Object key) {
// 从缓存中恢复状态
return this.store.get(key);
}
@Override
public void put(Object key, Object value) {
// 保存状态到缓存
this.store.put(key, toStoreValue(value));
}
@Override
public ValueWrapper putIfAbsent(Object key, Object value) {
// 条件保存,类似备忘录的条件创建
Object existing = this.store.putIfAbsent(key, toStoreValue(value));
return toValueWrapper(existing);
}
}
具体使用场景
1. 文本编辑器撤销/重做功能
// 文本编辑器备忘录模式实现
public class TextEditor {
private String content;
private Caretaker caretaker = new Caretaker();
public void setContent(String content) {
// 保存当前状态
caretaker.saveMemento(createMemento());
this.content = content;
}
public void undo() {
Memento memento = caretaker.undo();
if (memento != null) {
restoreMemento(memento);
}
}
public void redo() {
Memento memento = caretaker.redo();
if (memento != null) {
restoreMemento(memento);
}
}
private Memento createMemento() {
return new Memento(content);
}
private void restoreMemento(Memento memento) {
this.content = memento.getContent();
}
}
2. 游戏存档系统
// 游戏状态备忘录
public class GameState {
private int level;
private int score;
private String playerName;
private List<String> inventory;
public GameMemento saveGame() {
return new GameMemento(level, score, playerName, new ArrayList<>(inventory));
}
public void loadGame(GameMemento memento) {
this.level = memento.getLevel();
this.score = memento.getScore();
this.playerName = memento.getPlayerName();
this.inventory = new ArrayList<>(memento.getInventory());
}
}
// 游戏管理器
public class GameManager {
private GameState currentState;
private List<GameMemento> saveSlots = new ArrayList<>();
public void saveGame(int slot) {
if (slot >= 0 && slot < saveSlots.size()) {
saveSlots.set(slot, currentState.saveGame());
} else {
saveSlots.add(currentState.saveGame());
}
}
public void loadGame(int slot) {
if (slot >= 0 && slot < saveSlots.size()) {
currentState.loadGame(saveSlots.get(slot));
}
}
}
3. 数据库事务回滚
// 数据库操作备忘录
public class DatabaseTransaction {
private List<DatabaseOperation> operations = new ArrayList<>();
private Map<String, Object> originalValues = new HashMap<>();
public void executeUpdate(String sql, Object... params) {
// 保存原始值
saveOriginalValues(sql, params);
// 执行更新
operations.add(new UpdateOperation(sql, params));
}
public void rollback() {
// 恢复原始状态
for (int i = operations.size() - 1; i >= 0; i--) {
operations.get(i).rollback(originalValues);
}
operations.clear();
}
private void saveOriginalValues(String sql, Object... params) {
// 查询并保存原始值
String key = generateKey(sql, params);
Object originalValue = queryOriginalValue(sql, params);
originalValues.put(key, originalValue);
}
}
4. 配置管理系统
// 配置备忘录模式
public class ConfigurationManager {
private Map<String, Object> currentConfig;
private Stack<ConfigurationMemento> history = new Stack<>();
public void updateConfiguration(String key, Object value) {
// 保存当前配置状态
history.push(createMemento());
// 更新配置
currentConfig.put(key, value);
}
public void rollbackConfiguration() {
if (!history.isEmpty()) {
ConfigurationMemento memento = history.pop();
restoreMemento(memento);
}
}
private ConfigurationMemento createMemento() {
return new ConfigurationMemento(new HashMap<>(currentConfig));
}
private void restoreMemento(ConfigurationMemento memento) {
this.currentConfig = new HashMap<>(memento.getConfig());
}
}
5. 表单数据恢复
// 表单备忘录模式
public class FormManager {
private Map<String, Object> formData;
private List<FormMemento> history = new ArrayList<>();
private int currentIndex = -1;
public void saveFormState() {
// 删除当前位置之后的历史
if (currentIndex < history.size() - 1) {
history = history.subList(0, currentIndex + 1);
}
// 保存当前状态
FormMemento memento = new FormMemento(new HashMap<>(formData));
history.add(memento);
currentIndex++;
}
public void undo() {
if (currentIndex > 0) {
currentIndex--;
restoreFormState(history.get(currentIndex));
}
}
public void redo() {
if (currentIndex < history.size() - 1) {
currentIndex++;
restoreFormState(history.get(currentIndex));
}
}
private void restoreFormState(FormMemento memento) {
this.formData = new HashMap<>(memento.getFormData());
// 更新UI显示
updateFormUI();
}
}
面试高频点
1. 基本概念类
Q: 什么是备忘录模式?
A: 备忘录模式是一种行为型设计模式,它允许在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后可以将该对象恢复到原先保存的状态。
Q: 备忘录模式的核心组件有哪些?
A:
- Originator(发起人):需要保存状态的对象
- Memento(备忘录):存储发起人内部状态的对象
- Caretaker(管理者):负责保存和恢复备忘录的对象
2. 设计原理类
Q: 备忘录模式如何保证封装性?
A:
// 通过包私有或内部类限制访问
class Memento {
private final String state;
// 只有同一个包或内部类能访问
Memento(String state) {
this.state = state;
}
String getState() {
return state;
}
}
// Originator控制Memento的创建和访问
public class Originator {
public Memento createMemento() {
return new Memento(this.state);
}
public void restoreMemento(Memento memento) {
this.state = memento.getState();
}
}
Q: 备忘录模式与命令模式的区别?
A:
- 备忘录模式:关注对象状态的保存和恢复
- 命令模式:关注请求的封装和调用
- 备忘录模式:状态是数据,Memento存储数据
- 命令模式:状态是操作,Command封装操作
3. 实现细节类
Q: 如何实现深拷贝的备忘录?
A:
public class Originator {
private List<String> stateList;
public Memento createMemento() {
// 深拷贝列表
List<String> copyList = new ArrayList<>();
for (String item : stateList) {
copyList.add(new String(item));
}
return new Memento(copyList);
}
public void restoreMemento(Memento memento) {
// 深拷贝恢复
this.stateList = new ArrayList<>();
for (String item : memento.getStateList()) {
this.stateList.add(new String(item));
}
}
}
Q: 如何管理多个历史状态?
A:
public class Caretaker {
private List<Memento> mementos = new ArrayList<>();
private int currentIndex = -1;
private static final int MAX_HISTORY = 50;
public void saveMemento(Memento memento) {
// 删除当前位置之后的历史
if (currentIndex < mementos.size() - 1) {
mementos = mementos.subList(0, currentIndex + 1);
}
mementos.add(memento);
currentIndex++;
// 限制历史数量
if (mementos.size() > MAX_HISTORY) {
mementos.remove(0);
currentIndex--;
}
}
public Memento undo() {
if (currentIndex > 0) {
currentIndex--;
return mementos.get(currentIndex);
}
return null;
}
public Memento redo() {
if (currentIndex < mementos.size() - 1) {
currentIndex++;
return mementos.get(currentIndex);
}
return null;
}
}
4. 性能优化类
Q: 备忘录模式可能遇到哪些性能问题?
A:
- 内存占用:大量历史状态占用内存
- 深拷贝开销:复杂对象的深拷贝成本高
- 序列化开销:网络传输或持久化时的序列化成本
解决方案:
public class OptimizedCaretaker {
private static final int MAX_MEMENTOS = 20;
private List<Memento> mementos = new ArrayList<>();
public void saveMemento(Memento memento) {
// 限制历史数量
if (mementos.size() >= MAX_MEMENTOS) {
mementos.remove(0);
}
mementos.add(memento);
}
// 使用序列化进行持久化
public void saveToFile(String filename) throws IOException {
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filename))) {
out.writeObject(mementos);
}
}
public void loadFromFile(String filename) throws IOException, ClassNotFoundException {
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(filename))) {
mementos = (List<Memento>) in.readObject();
}
}
}
5. 实际应用类
Q: 在Spring框架中,哪些地方使用了备忘录模式的思想?
A:
- 事务管理:保存事务开始前的状态,支持回滚
- 会话管理:HttpSession保存用户状态
- 配置管理:ConfigurationProperties的状态保存
- 缓存管理:缓存状态的保存和恢复
Q: 备忘录模式在微服务架构中的应用?
A:
// 分布式状态管理
public class DistributedStateManager {
private RedisTemplate<String, Object> redisTemplate;
public void saveState(String serviceId, String stateId, Object state) {
// 保存到Redis
String key = "state:" + serviceId + ":" + stateId;
redisTemplate.opsForValue().set(key, state, Duration.ofHours(24));
}
public Object restoreState(String serviceId, String stateId) {
String key = "state:" + serviceId + ":" + stateId;
return redisTemplate.opsForValue().get(key);
}
// 支持状态版本管理
public void saveStateWithVersion(String serviceId, String stateId, Object state, int version) {
String key = "state:" + serviceId + ":" + stateId + ":" + version;
redisTemplate.opsForValue().set(key, state, Duration.ofDays(7));
}
}
6. 设计权衡类
Q: 备忘录模式的优缺点?
A:
优点:
- 保护封装性,不暴露对象内部实现
- 提供状态恢复机制
- 支持撤销/重做功能
- 状态管理集中化
缺点:
- 内存占用较大
- 复杂对象的深拷贝成本高
- 状态管理复杂度增加
- 可能影响性能
Q: 什么时候不应该使用备忘录模式?
A:
- 状态变化频繁:每次变化都保存状态会消耗大量内存
- 对象状态简单:简单状态不需要复杂的状态管理
- 性能要求极高:状态保存和恢复可能影响性能
- 状态不可逆:某些状态一旦改变就不应该恢复
使用总结
适用场景总结
1. 必须使用备忘录模式的场景
- 撤销/重做功能:文本编辑器、图形编辑器
- 游戏存档系统:保存和加载游戏状态
- 事务回滚:数据库事务、业务操作回滚
- 配置管理:系统配置的保存和恢复
- 表单数据恢复:用户输入数据的恢复
2. 可以考虑使用的场景
- 状态快照:定期保存系统状态
- 调试支持:保存调试时的状态信息
- 版本控制:对象状态的版本管理
- 备份恢复:重要数据的备份和恢复
最佳实践建议
1. 状态管理策略
// 建议:实现状态管理策略
public interface StateManagementStrategy {
void saveState(Object state);
Object restoreState();
boolean canUndo();
boolean canRedo();
}
// 内存策略
public class MemoryStateStrategy implements StateManagementStrategy {
private Stack<Object> stateStack = new Stack<>();
@Override
public void saveState(Object state) {
stateStack.push(state);
}
@Override
public Object restoreState() {
return stateStack.pop();
}
}
// 持久化策略
public class PersistentStateStrategy implements StateManagementStrategy {
private String filePath;
@Override
public void saveState(Object state) {
// 保存到文件
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(filePath))) {
out.writeObject(state);
} catch (IOException e) {
throw new RuntimeException("Failed to save state", e);
}
}
}
2. 性能优化建议
// 建议:实现状态压缩
public class CompressedMemento implements Serializable {
private byte[] compressedState;
public CompressedMemento(Object state) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(state);
oos.close();
// 压缩数据
this.compressedState = compress(baos.toByteArray());
} catch (IOException e) {
throw new RuntimeException("Failed to compress state", e);
}
}
public Object getState() {
try {
// 解压缩数据
byte[] decompressed = decompress(compressedState);
ByteArrayInputStream bais = new ByteArrayInputStream(decompressed);
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException("Failed to decompress state", e);
}
}
}
3. 错误处理建议
// 建议:实现错误处理机制
public class RobustCaretaker {
private List<Memento> mementos = new ArrayList<>();
private int currentIndex = -1;
public void saveMemento(Memento memento) {
try {
// 验证备忘录有效性
if (memento == null) {
throw new IllegalArgumentException("Memento cannot be null");
}
// 保存备忘录
if (currentIndex < mementos.size() - 1) {
mementos = mementos.subList(0, currentIndex + 1);
}
mementos.add(memento);
currentIndex++;
} catch (Exception e) {
// 记录错误但不影响程序运行
System.err.println("Failed to save memento: " + e.getMessage());
}
}
public Memento undo() {
try {
if (currentIndex > 0) {
currentIndex--;
return mementos.get(currentIndex);
}
} catch (Exception e) {
System.err.println("Failed to undo: " + e.getMessage());
}
return null;
}
}
与其他模式的结合
1. 与命令模式结合
// 可撤销的命令
public abstract class UndoableCommand implements Command {
protected Memento memento;
@Override
public void execute() {
// 保存执行前状态
memento = createMemento();
doExecute();
}
public void undo() {
if (memento != null) {
restoreMemento(memento);
}
}
protected abstract void doExecute();
protected abstract Memento createMemento();
protected abstract void restoreMemento(Memento memento);
}
2. 与状态模式结合
// 状态历史管理
public class StateHistoryManager {
private List<StateMemento> stateHistory = new ArrayList<>();
private int currentStateIndex = -1;
public void saveState(State currentState) {
StateMemento memento = new StateMemento(currentState);
stateHistory.add(memento);
currentStateIndex++;
}
public State restorePreviousState() {
if (currentStateIndex > 0) {
currentStateIndex--;
return stateHistory.get(currentStateIndex).getState();
}
return null;
}
}
总结
备忘录模式是一个强大的设计模式,特别适用于需要状态保存和恢复的场景。通过合理的设计和优化,可以有效地管理对象状态,提供良好的用户体验。在实际应用中,需要根据具体需求选择合适的实现策略,并注意性能优化和错误处理。
3479

被折叠的 条评论
为什么被折叠?



