当前位置: 首页 > news >正文

设计模式精讲 Day 18:备忘录模式(Memento Pattern)

【设计模式精讲 Day 18】备忘录模式(Memento Pattern)


文章内容

开篇

在“设计模式精讲”系列的第18天,我们来探讨备忘录模式(Memento Pattern)。这是一种行为型设计模式,其核心思想是在不破坏封装性的前提下,捕获并外部化一个对象的内部状态,以便在需要时恢复该对象的状态

备忘录模式广泛应用于需要支持撤销操作、快照功能、版本控制等场景中。它能够帮助开发者在不暴露对象内部结构的前提下,实现对对象状态的保存与恢复,从而提升系统的灵活性和可维护性。

本篇文章将从理论到实践全面解析备忘录模式,包括其定义、结构、适用场景、实现方式、优缺点分析,并结合真实项目案例进行深入讲解,帮助你掌握这一实用的设计模式。


模式定义

备忘录模式是一种行为型设计模式,它允许在不暴露对象内部状态的情况下,捕获并存储该对象的状态,以便后续恢复。通过这种方式,可以实现对象的回退、撤销等功能。

核心思想

  • 保存状态:在对象发生变化之前,将其状态保存下来。
  • 恢复状态:在需要的时候,将对象恢复到之前保存的状态。
  • 封装性:状态的保存和恢复过程对外部是透明的,不依赖于对象的内部实现。

模式结构

备忘录模式包含以下三个关键角色:

角色职责
Originator(发起人)负责创建自身的状态快照(备忘录),并能根据备忘录恢复自身状态。
Memento(备忘录)存储Originator的内部状态,通常是一个不可变对象。
Caretaker(管理者)负责保存备忘录,但不会修改或查看备忘录的内容。

UML类图描述(文字版):

  • Originator 类有一个方法 createMemento(),用于生成一个 Memento 对象。
  • Memento 类包含 Originator 的状态信息,提供访问器方法供 Originator 使用。
  • Caretaker 类维护一个 Memento 的集合或单个实例,并通过 setMemento() 方法将 Memento 传递给 Originator,以恢复其状态。

适用场景

备忘录模式适用于以下几种典型场景:

场景说明
撤销/重做功能在编辑器、IDE等应用中,用户执行操作后可以通过备忘录模式快速回退到之前的版本。
版本控制在文档、代码等系统中,记录不同版本的状态,便于回溯或对比。
事务处理在数据库事务中,如果事务失败,可以通过备忘录模式回滚到事务开始前的状态。
游戏存档游戏中保存玩家当前状态,便于下次继续游戏。

这些场景的核心需求是:在不暴露对象内部状态的前提下,实现状态的保存与恢复


实现方式

下面是一个完整的Java实现示例,展示如何使用备忘录模式。

1. Memento 类

public class Memento {private final String state;public Memento(String state) {this.state = state;}public String getState() {return state;}
}

2. Originator 类

public class Originator {private String state;public void setState(String state) {this.state = state;}public String getState() {return state;}public Memento createMemento() {return new Memento(this.state);}public void restoreMemento(Memento memento) {this.state = memento.getState();}
}

3. Caretaker 类

import java.util.ArrayList;
import java.util.List;public class Caretaker {private List<Memento> mementos = new ArrayList<>();public void addMemento(Memento memento) {mementos.add(memento);}public Memento getMemento(int index) {return mementos.get(index);}
}

4. 测试类

public class MementoPatternDemo {public static void main(String[] args) {Originator originator = new Originator();Caretaker caretaker = new Caretaker();originator.setState("State 1");System.out.println("Initial State: " + originator.getState());caretaker.addMemento(originator.createMemento());originator.setState("State 2");System.out.println("Current State: " + originator.getState());caretaker.addMemento(originator.createMemento());originator.setState("State 3");System.out.println("Current State: " + originator.getState());caretaker.addMemento(originator.createMemento());System.out.println("Restoring to State 2...");originator.restoreMemento(caretaker.getMemento(1));System.out.println("Current State after restore: " + originator.getState());}
}
输出结果:
Initial State: State 1
Current State: State 2
Current State: State 3
Restoring to State 2...
Current State after restore: State 2

关键设计决策说明

  • Memento 是一个不可变对象,确保状态一旦创建就不会被修改。
  • Originator 只负责状态的保存和恢复,不关心如何存储或管理备忘录。
  • Caretaker 仅负责保存和获取备忘录,不参与状态的修改,保证了封装性。

工作原理

备忘录模式的工作原理基于状态的外部化存储。当对象处于某个特定状态时,调用 createMemento() 方法,将当前状态复制为一个备忘录对象。之后,可以通过 restoreMemento() 方法将对象恢复到该状态。

这种机制避免了直接访问对象内部属性,保持了良好的封装性。同时,由于备忘录本身是只读的,也防止了外部对状态的非法修改。


优缺点分析

优点缺点
封装性好:状态保存和恢复过程对外部透明,不暴露内部结构。内存消耗大:如果对象状态复杂或频繁保存,会占用较多内存。
支持撤销操作:非常适合实现撤销、重做等功能。无法跨平台恢复:备忘录通常依赖于具体实现,难以跨语言或跨系统使用。
易于扩展:可以在不修改原有代码的基础上添加新的状态保存策略。实现成本较高:对于复杂对象,需要额外编写状态保存和恢复逻辑。

案例分析

场景:文本编辑器中的撤销功能

在开发一个简单的文本编辑器时,用户可能希望支持“撤销”操作。例如,用户输入了一段文字,然后删除了部分内容,这时他希望恢复到之前的状态。

问题
  • 如何在不暴露编辑器内部状态的前提下,实现撤销功能?
  • 如何避免每次操作都重新构建整个文本内容?
解决方案

使用备忘录模式,每次用户执行操作后,将当前文本内容保存为一个备忘录。当用户点击“撤销”时,从备忘录中恢复上一个状态。

实现代码(简化版)
class TextEditor {private String content;public void setContent(String content) {this.content = content;}public String getContent() {return content;}public Memento saveState() {return new Memento(content);}public void restoreState(Memento memento) {this.content = memento.getState();}
}class EditorCaretaker {private List<Memento> history = new ArrayList<>();public void save(TextEditor editor) {history.add(editor.saveState());}public Memento getLastState() {if (history.isEmpty()) return null;return history.remove(history.size() - 1);}
}
使用示例
public class TextEditorDemo {public static void main(String[] args) {TextEditor editor = new TextEditor();EditorCaretaker caretaker = new EditorCaretaker();editor.setContent("Hello, World!");caretaker.save(editor);editor.setContent("Hello, CSDN!");caretaker.save(editor);System.out.println("Current content: " + editor.getContent());// 撤销到最后一个状态Memento lastState = caretaker.getLastState();if (lastState != null) {editor.restoreState(lastState);System.out.println("After undo: " + editor.getContent());}}
}
输出结果:
Current content: Hello, CSDN!
After undo: Hello, World!

这个案例展示了备忘录模式在实际项目中的应用价值,特别是在需要支持撤销操作的场景中非常实用。


与其他模式的关系

备忘录模式常与以下模式配合使用:

模式说明
命令模式(Command Pattern)命令模式用于封装请求,而备忘录模式用于保存状态。两者结合可用于实现撤销操作。
迭代器模式(Iterator Pattern)迭代器模式用于遍历集合,而备忘录模式可用于保存遍历过程中的状态。
状态模式(State Pattern)状态模式用于管理对象的不同状态,而备忘录模式可用于保存当前状态。两者结合可用于实现更复杂的业务逻辑。

在某些场景下,备忘录模式还可以与原型模式结合使用,实现对象的深拷贝和状态还原。


总结

本篇文章详细讲解了备忘录模式的定义、结构、适用场景、实现方式及优缺点分析,并通过一个文本编辑器的案例展示了其实际应用场景。备忘录模式在实现撤销、版本控制、事务回滚等场景中具有重要价值。

在面向对象设计中,备忘录模式体现了封装性单一职责原则,有助于提高系统的可维护性和可扩展性。

下一篇预告

明天我们将进入“设计模式精讲”的第19天,讲解观察者模式(Observer Pattern),它是实现对象间一对多依赖关系的重要设计模式,广泛应用于事件驱动系统中。敬请期待!


文章标签

design-patterns,memento-pattern,java-design-patterns,software-architecture,java-programming


文章简述

本文是“设计模式精讲”系列的第18天,重点讲解备忘录模式(Memento Pattern)。备忘录模式是一种行为型设计模式,允许在不破坏封装性的前提下,捕获并保存对象的状态,以便在需要时恢复。文章从理论到实践,系统地介绍了该模式的定义、结构、适用场景、实现方式以及优缺点分析,并通过一个文本编辑器的案例展示了其在实际项目中的应用。此外,还讨论了备忘录模式与其他设计模式的关系,如命令模式、状态模式等。通过本文的学习,读者可以掌握如何在实际开发中灵活运用备忘录模式,提升系统的可维护性和可扩展性。


进一步学习资料

  1. Design Patterns: Elements of Reusable Object-Oriented Software
  2. Head First Design Patterns
  3. Java Design Patterns - A Tutorial
  4. Java 中的备忘录模式详解
  5. Effective Java by Joshua Bloch

核心设计思想总结

通过本篇文章,我们掌握了以下核心设计思想:

  • 封装性:备忘录模式通过将状态保存在外部对象中,避免了直接暴露对象的内部状态。
  • 状态管理:支持对象状态的保存与恢复,适用于撤销、版本控制等场景。
  • 解耦设计OriginatorCaretaker 之间职责分离,降低了耦合度。
  • 可扩展性:备忘录模式易于扩展,可以在不修改原有代码的基础上增加新的状态保存策略。

在实际项目中,你可以将备忘录模式用于实现撤销功能、游戏存档、事务回滚等场景,从而提升系统的灵活性和用户体验。

相关文章:

  • FastAPI路由管理APIRouter实战指南
  • 广度优先搜索BFS(广搜)复习(c++)
  • 【智能协同云图库】智能协同云图库第三弹:基于腾讯云 COS 对象存储—开发图片模块
  • 电子计数跳绳原型
  • 如何撰写有价值的项目复盘报告
  • 深入剖析Nacos服务发现与注册,及如何基于LoadBalancer实现负载均衡
  • Tomcat 安装使用教程
  • 第10篇 图像语义分割和目标检测介绍
  • OpenCV 4.10.0 移植
  • Kafka与RabbitMQ相比有什么优势?
  • 第七节 矩阵键盘模块
  • MCP Chart Server服务本地部署案例
  • 咸虾米项目总结1--const用法
  • LeetCode Hot 100 最大子数组和
  • 推荐几本关于网络安全的书
  • 动态执行js
  • 系统架构设计师备考之架构设计专业知识
  • 软考 系统架构设计师系列知识点之杂项集萃(100)
  • 海量数据存储与分析:HBase、ClickHouse、Doris三款数据库对比
  • http相关网络问题面试怎么答