👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
👍 欢迎点赞、收藏、关注,跟上我的更新节奏
🎵 当你的天空突然下了大雨,那是我在为你炸乌云
文章目录
- 一、入门
- 什么是备忘录模式?
- 为什么需要备忘录模式?
- 如何实现备忘录模式?
- 二、备忘录模式在框架源码中的运用
- Spring 框架中的事务管理
- 三、总结
- 备忘录模式的优点
- 备忘录模式的缺点
- 备忘录模式的适用场景
一、入门
什么是备忘录模式?
备忘录模式(Memento Pattern)是一种行为设计模式,用于在不破坏封装性的前提下,捕获并外部化一个对象的内部状态,以便在需要时恢复该状态。它通常用于实现撤销操作或保存对象的历史状态。
为什么需要备忘录模式?
假设我们正在开发一个简单的文本编辑器,支持以下功能:
- 用户可以输入文本。
- 用户可以保存当前文本状态。
- 用户可以撤销操作,恢复到之前保存的状态。
我们实现的代码如下
// 文本编辑器类
class Editor {private String content;public void setContent(String content) {this.content = content;}public String getContent() {return content;}// 保存状态(直接返回内部状态)public String save() {return content;}// 恢复状态(直接设置内部状态)public void restore(String savedContent) {this.content = savedContent;}
}
客户端测试
// 客户端代码
public class TextEditorWithoutMemento {public static void main(String[] args) {Editor editor = new Editor();// 用户输入文本editor.setContent("First draft");System.out.println("Current Content: " + editor.getContent());// 保存状态String savedState = editor.save();// 用户修改文本editor.setContent("Second draft");System.out.println("Updated Content: " + editor.getContent());// 撤销操作,恢复到之前的状态editor.restore(savedState);System.out.println("Restored Content: " + editor.getContent());}
}
存在的问题
- 破坏封装性:
Editor
的content
状态通过save()
和restore()
方法直接暴露给外部,外部代码可以随意修改状态。 - 耦合性高:撤销逻辑直接依赖
Editor
的内部状态,如果Editor
的状态结构发生变化,撤销逻辑也需要修改。 - 难以扩展:如果需要支持多次撤销(保存多个历史状态),代码会变得复杂且难以维护。
如何实现备忘录模式?
备忘录模式的核心角色
- Originator(发起人):
- 负责创建备忘录,记录当前时刻的内部状态。
- 可以使用备忘录恢复内部状态。
- Memento(备忘录):
- 存储发起人的内部状态。
- 通常只允许发起人访问其内部状态,以防止其他对象修改。
- Caretaker(管理者):
- 负责保存备忘录,但不能修改或检查其内容。
- 提供保存和恢复备忘录的功能。
【案例】文本编辑 - 改
Memento(备忘录):EditorMemento
类,保存Editor
的状态
class EditorMemento {private final String content;public EditorMemento(String content) {this.content = content;}public String getContent() {return content;}
}
Originator(发起人):Editor
类
class Editor {private String content;public void setContent(String content) {this.content = content;}public String getContent() {return content;}// 创建备忘录public EditorMemento save() {return new EditorMemento(content);}// 从备忘录恢复状态public void restore(EditorMemento memento) {this.content = memento.getContent();}
}
Caretaker(管理者):History
类
class History {private final List<EditorMemento> mementos = new ArrayList<>();public void push(EditorMemento memento) {mementos.add(memento);}public EditorMemento pop() {if (mementos.isEmpty()) {return null;}return mementos.remove(mementos.size() - 1);}
}
客户端代码
public class TextEditorWithMemento {public static void main(String[] args) {Editor editor = new Editor();History history = new History();// 用户输入文本editor.setContent("First draft");history.push(editor.save()); // 保存状态System.out.println("Current Content: " + editor.getContent());// 用户修改文本editor.setContent("Second draft");history.push(editor.save()); // 保存状态System.out.println("Updated Content: " + editor.getContent());// 用户再次修改文本editor.setContent("Third draft");System.out.println("Updated Content: " + editor.getContent());// 撤销操作editor.restore(history.pop()); // 恢复到第二次保存的状态System.out.println("Restored Content: " + editor.getContent());// 再次撤销操作editor.restore(history.pop()); // 恢复到第一次保存的状态System.out.println("Restored Content: " + editor.getContent());}
}
二、备忘录模式在框架源码中的运用
Spring 框架中的事务管理
Spring 框架中的事务管理也使用了备忘录模式的思想。在事务回滚时,Spring 需要恢复对象的状态。
备忘录(Memento):TransactionStatus
,TransactionStatus
保存了事务的当前状态
以下是精简版代码的
public interface TransactionStatus extends SavepointManager, Flushable {boolean isNewTransaction(); // 是否是新事务boolean hasSavepoint(); // 是否有保存点void setRollbackOnly(); // 标记事务为回滚boolean isRollbackOnly(); // 是否需要回滚boolean isCompleted(); // 事务是否已完成
}
管理者(Caretaker):PlatformTransactionManager
PlatformTransactionManager
是 Spring 事务管理的核心接口,负责管理事务的生命周期。它通过 getTransaction()
方法获取事务状态(TransactionStatus
),并在事务提交或回滚时恢复状态。
public interface PlatformTransactionManager {TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;void commit(TransactionStatus status) throws TransactionException;void rollback(TransactionStatus status) throws TransactionException;
}
发起人(Originator):TransactionTemplate
是Spring
提供的一个工具类,用于简化事务的编程式管理。它封装了事务的执行逻辑,并在事务执行过程中创建和恢复事务状态。
public class TransactionTemplate extends DefaultTransactionDefinition implements TransactionOperations {private PlatformTransactionManager transactionManager;@Overridepublic <T> T execute(TransactionCallback<T> action) throws TransactionException {// 获取事务状态TransactionStatus status = this.transactionManager.getTransaction(this);T result;try {// 执行业务逻辑result = action.doInTransaction(status);} catch (RuntimeException | Error ex) {// 回滚事务rollbackOnException(status, ex);throw ex;}// 提交事务this.transactionManager.commit(status);return result;}private void rollbackOnException(TransactionStatus status, Throwable ex) throws TransactionException {if (status.isNewTransaction()) {this.transactionManager.rollback(status);}}
}
三、总结
备忘录模式的优点
- 封装性好:
- 备忘录模式将对象的状态保存在一个独立的
Memento
对象中,避免了直接暴露对象的内部状态,保持了对象的封装性。
- 备忘录模式将对象的状态保存在一个独立的
- 简化状态管理:
- 通过将状态管理的逻辑集中到
Caretaker
中,避免了状态管理代码分散在业务逻辑中,使代码更清晰、更易维护。
- 通过将状态管理的逻辑集中到
- 支持撤销/重做功能:
- 备忘录模式天然适合实现撤销和重做功能,通过保存多个 Memento 对象,可以轻松恢复到之前的状态。
- 扩展性强:
- 如果需要支持多次撤销或保存多个历史状态,只需在
Caretaker
中维护多个Memento
对象即可。
- 如果需要支持多次撤销或保存多个历史状态,只需在
- 解耦:
- 备忘录模式将状态保存和恢复的逻辑与业务逻辑解耦,使得代码更易于复用和扩展。
备忘录模式的缺点
- 内存消耗:
- 如果需要保存大量的状态或状态数据较大,备忘录模式可能会占用较多的内存。
- 性能开销:
- 频繁地创建和恢复
Memento
对象可能会带来一定的性能开销,尤其是在状态数据较大的情况下。
- 频繁地创建和恢复
- 复杂性增加:
- 对于简单的场景,引入备忘录模式可能会增加代码的复杂性,显得“杀鸡用牛刀”。
- 状态暴露风险:
- 如果
Memento
对象的设计不当,可能会导致状态暴露给外部代码,破坏封装性。
- 如果
备忘录模式的适用场景
- 撤销/重做功能:
- 需要实现撤销和重做功能的场景,如文本编辑器、绘图工具等。
- 事务管理:
- 需要保存和恢复对象状态的场景,如数据库事务管理、游戏存档等。
- 历史记录:
- 需要保存对象的历史状态以供后续使用的场景,如操作日志、版本控制等。
- 状态快照:
- 需要在某个时间点保存对象的状态,并在后续恢复的场景,如游戏中的保存点、虚拟机快照等。
- 复杂对象的状态管理:
- 对于状态结构复杂的对象,备忘录模式可以简化状态管理逻辑。