Java设计模式之备忘录模式详解

news2025/6/5 19:06:43

Java设计模式之备忘录模式详解


一、备忘录模式核心思想

核心目标捕获对象内部状态并在需要时恢复,同时不破坏对象的封装性。如同游戏存档系统,允许玩家保存当前进度并在需要时回退到之前的状态。


二、备忘录模式类图(Mermaid)

创建
存储
Originator
-state: String
+saveToMemento() : Memento
+restoreFromMemento(Memento)
+setState(String)
+getState() : String
Memento
-state: String
+getState() : String
+setState(String)
Caretaker
-mementos: List
+addMemento(Memento)
+getMemento(int) : Memento

三、代码实现示例

1. 文本编辑器撤销功能

// 备忘录类(存储编辑器状态)
class TextMemento {
    private final String text;
    private final int cursorPosition;
    
    public TextMemento(String text, int cursorPosition) {
        this.text = text;
        this.cursorPosition = cursorPosition;
    }
    
    public String getText() {
        return text;
    }
    
    public int getCursorPosition() {
        return cursorPosition;
    }
}

// 原发器:文本编辑器
class TextEditor {
    private StringBuilder text = new StringBuilder();
    private int cursorPosition = 0;
    
    public void type(String words) {
        text.insert(cursorPosition, words);
        cursorPosition += words.length();
        System.out.println("当前文本: " + text);
    }
    
    public void moveCursor(int position) {
        cursorPosition = Math.max(0, Math.min(position, text.length()));
        System.out.println("光标移动到: " + cursorPosition);
    }
    
    public TextMemento save() {
        return new TextMemento(text.toString(), cursorPosition);
    }
    
    public void restore(TextMemento memento) {
        this.text = new StringBuilder(memento.getText());
        this.cursorPosition = memento.getCursorPosition();
        System.out.println("恢复文本: " + text);
    }
    
    public void printStatus() {
        System.out.println("文本: " + text);
        System.out.println("光标位置: " + cursorPosition);
    }
}

// 管理者:历史记录
class History {
    private List<TextMemento> states = new ArrayList<>();
    
    public void push(TextMemento state) {
        states.add(state);
    }
    
    public TextMemento pop() {
        if (states.isEmpty()) return null;
        return states.remove(states.size() - 1);
    }
    
    public TextMemento get(int index) {
        return states.get(index);
    }
}

// 客户端调用
public class Client {
    public static void main(String[] args) {
        TextEditor editor = new TextEditor();
        History history = new History();
        
        // 编辑文本
        editor.type("设计模式");
        history.push(editor.save());  // 保存状态1
        
        editor.type("备忘录");
        history.push(editor.save());  // 保存状态2
        
        editor.type("示例");
        System.out.println("\n当前状态:");
        editor.printStatus();
        
        // 撤销到上一步
        System.out.println("\n撤销操作:");
        editor.restore(history.pop());  // 恢复状态2
        editor.printStatus();
        
        // 再撤销一步
        System.out.println("\n再次撤销:");
        editor.restore(history.pop());  // 恢复状态1
        editor.printStatus();
    }
}

四、模式优缺点分析

✅ 优势

  • 状态封装:不暴露对象内部实现细节
  • 撤销/重做支持:轻松实现历史记录功能
  • 状态快照:支持任意时刻状态保存
  • 符合单一职责:状态管理职责分离

❌ 缺点

  • 内存消耗:大量状态保存可能导致内存占用高
  • 性能影响:大对象状态保存/恢复可能耗时
  • 复杂状态处理:嵌套对象状态保存较复杂

五、典型应用场景

  1. 文本编辑器:撤销/重做功能实现
  2. 游戏开发:保存/加载游戏进度
  3. 事务回滚:数据库操作回退
  4. 软件配置:保存和恢复用户设置
  5. 绘图软件:操作历史记录
  6. 状态机:回退到之前状态

六、Mermaid序列图(状态保存与恢复)

Client Originator Caretaker Memento 修改状态 saveToMemento() 创建备忘录(状态) 返回备忘录 addMemento(备忘录) getMemento() 备忘录 restoreFromMemento(备忘录) getState() 状态值 Client Originator Caretaker Memento

七、备忘录模式 vs 其他模式

对比模式核心区别
命令模式封装操作请求,可支持撤销
状态模式对象行为随状态改变
原型模式克隆对象而非保存状态

八、实际框架应用案例

1. Java Swing的UndoManager

管理
«interface»
UndoableEdit
+undo()
+redo()
AbstractUndoableEdit
UndoManager
CompoundEdit

2. Spring框架的事务管理

@Transactional
public void transferMoney(Account from, Account to, double amount) {
    // 事务开始时创建备忘录(保存点)
    savepoint = TransactionAspectSupport.currentTransactionStatus().createSavepoint();
    
    try {
        from.withdraw(amount);
        to.deposit(amount);
    } catch (Exception e) {
        // 回滚到保存点
        TransactionAspectSupport.currentTransactionStatus().rollbackToSavepoint(savepoint);
    }
}

九、高级应用技巧

1. 增量备忘录(节省内存)

class IncrementalMemento {
    private final String diff;  // 只存储变化部分
    
    public IncrementalMemento(String diff) {
        this.diff = diff;
    }
    
    public String apply(String base) {
        // 应用差异到基础状态
        return base + diff;
    }
}

2. 多级撤销/重做栈

class HistoryManager {
    private Stack<Memento> undoStack = new Stack<>();
    private Stack<Memento> redoStack = new Stack<>();
    
    public void save(Memento state) {
        undoStack.push(state);
        redoStack.clear();
    }
    
    public Memento undo() {
        if (!undoStack.isEmpty()) {
            Memento state = undoStack.pop();
            redoStack.push(state);
            return undoStack.isEmpty() ? null : undoStack.peek();
        }
        return null;
    }
    
    public Memento redo() {
        if (!redoStack.isEmpty()) {
            Memento state = redoStack.pop();
            undoStack.push(state);
            return state;
        }
        return null;
    }
}

十、常见问题解答

Q1:如何保存复杂对象状态?

  • 序列化:实现Serializable接口
class ComplexMemento implements Serializable {
    private Object complexState;
}

Q2:如何处理外部资源引用?

使用深拷贝避免外部资源影响:

class ResourceMemento {
    private Resource resourceCopy;
    
    public ResourceMemento(Resource original) {
        this.resourceCopy = original.deepCopy();
    }
}

Q3:如何限制备忘录访问权限?

使用内部类实现封装:

class Originator {
    private String state;
    
    // 内部备忘录类
    class Memento {
        private String state;
        
        private Memento(String state) {
            this.state = state;
        }
        
        private String getState() {
            return state;
        }
    }
    
    public Memento save() {
        return new Memento(state);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2398353.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Azure DevOps Server 2022.2 补丁(Patch 5)

微软Azure DevOps Server的产品组在4月8日发布了2022.2 的第5个补丁。下载路径为&#xff1a;https://aka.ms/devops2022.2patch5 这个补丁的主要功能是修改了代理(Agent)二进制安装文件的下载路径&#xff1b;之前&#xff0c;微软使用这个CND(域名为vstsagentpackage.azuree…

手摸手还原vue3中reactive的get陷阱以及receiver的作用

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、实例是什么&#xff1f;二、new Prxoy三、实现代码1.引入代码2.读入数据 总结 前言 receiver不是为解决get陷阱而生&#xff0c;而是为解决Proxy中的this绑…

C++学习-入门到精通【13】标准库的容器和迭代器

C学习-入门到精通【13】标准库的容器和迭代器 目录 C学习-入门到精通【13】标准库的容器和迭代器一、标准模板库简介1.容器简介2.STL容器总览3.近容器4.STL容器的通用函数5.首类容器的通用typedef6.对容器元素的要求 二、迭代器简介1.使用istream_iterator输入&#xff0c;使用…

C# 面向对象特性

面向对象编程的三大基本特性是&#xff1a;封装、继承和多态。下面将详细介绍这三大特性在C#中的体现方式。 封装 定义&#xff1a;把对象的数据和操作代码组合在同一个结构中&#xff0c;这就是对象的封装性。 体现方式&#xff1a; 使用访问修饰符控制成员的可见性 通过属…

ElasticStack技术之logstash介绍

一、什么是Logstash Logstash 是 Elastic Stack&#xff08;ELK Stack&#xff09;中的一个开源数据处理管道工具&#xff0c;主要用于收集、解析、过滤和传输数据。它支持多种输入源&#xff0c;如文件、网络、数据库等&#xff0c;能够灵活地对数据进行处理&#xff0c;比如…

CI/CD 持续集成、持续交付、持续部署

CI/CD 是 持续集成&#xff08;Continuous Integration&#xff09; 和 持续交付/持续部署&#xff08;Continuous Delivery/Deployment&#xff09; 的缩写&#xff0c;代表现代软件开发中通过自动化流程快速、可靠地构建、测试和发布代码的实践。其核心目标是 减少人工干预、…

关于easyx头文件

一、窗口创建 &#xff08;1&#xff09;几种创建方式 #include<easyx.h>//easyx的头文件 #include<iostream> using namespace std;int main() {//创建一个500*500的窗口//参数为&#xff1a;长度&#xff0c;宽度&#xff0c;是否显示黑框&#xff08;无参为不…

django入门-orm数据库操作

一&#xff1a;下载数据库依赖项mysqlclient pip install mysqlclient 二&#xff1a;django配置文件配置数据库链接 路径&#xff1a;mysite2\mysite2\settings.py DATABASES {default: {ENGINE: django.db.backends.mysql,NAME: data, # 数据库名称USER: root, …

STM32外部中断(EXTI)以及旋转编码器的简介

一、外部中断机制概述 中断是指当主程序执行期间出现特定触发条件&#xff08;即中断源&#xff09;时&#xff0c;CPU将暂停当前任务&#xff0c;转而执行相应的中断服务程序&#xff08;ISR&#xff09;&#xff0c;待处理完成后恢复原程序的运行流程。该机制通过事件驱动…

双擎驱动:华为云数字人与DeepSeek大模型的智能交互升级方案

一、技术融合概述 华为云数字人 华为云数字人&#xff0c;全称&#xff1a;数字内容生产线 MetaStudio。数字内容生产线&#xff0c;提供数字人视频制作、视频直播、智能交互、企业代言等多种服务能力&#xff0c;使能千行百业降本增效。另外&#xff0c;数字内容生产线&#…

实验设计与分析(第6版,Montgomery)第5章析因设计引导5.7节思考题5.5 R语言解题

本文是实验设计与分析&#xff08;第6版&#xff0c;Montgomery著&#xff0c;傅珏生译) 第5章析因设计引导5.7节思考题5.5 R语言解题。主要涉及方差分析&#xff0c;正态假设检验&#xff0c;残差分析&#xff0c;交互作用图。 dataframe <-data.frame( wrapc(17,20,12,9,…

字节新出的MCP应用DeepSearch,有点意思。

大家好&#xff0c;我是苍何。 悄悄告诉你个事&#xff0c;昨天我去杭州参加字节火山方舟举办的开发者见面会了&#xff0c;你别说&#xff0c;还真有点刘姥姥进大观园的感觉&#x1f436; 现场真实体验完这次新发布的产品和模型&#xff0c;激动的忍不住想给大家做一波分享。…

期货反向跟单运营逻辑推导思路

期货反向跟单运营逻辑推导思路 很多刚接触期货反向跟的朋友第一印象就是&#xff1a;这绝对是一个完美的策略&#xff0c;在认知不到位的情况下就开始运营&#xff0c;结果就是赔的稀里哗啦。然后告诉身边所有的人&#xff0c;期货反向跟单不靠谱。 这就是一个很有意思的事情&…

使用 HTML + JavaScript 实现图片裁剪上传功能

本文将详细介绍一个基于 HTML 和 JavaScript 实现的图片裁剪上传功能。该功能支持文件选择、拖放上传、图片预览、区域选择、裁剪操作以及图片下载等功能&#xff0c;适用于需要进行图片处理的 Web 应用场景。 效果演示 项目概述 本项目主要包含以下核心功能&#xff1a; 文…

【灵动Mini-F5265-OB】vscode+gcc工程创建、下载、调试

【前言】 【灵动Mini-F5265-OB】在官方的例程中提供了mdk、IAR的开发环境&#xff0c;使用起来非常方便。有位大佬也提供了一个gcc的示例&#xff0c;但是我使用vscode的keil插件进行工程创建&#xff0c;但是提示pack是对不上的。所以我决定重新创建我的vscode来创建开发环境。…

现代语言模型中的分词算法全解:从基础到高级

基础分词&#xff08;Naive Tokenization&#xff09; 最简单的分词方式是基于空格将文本拆分为单词。这是许多自然语言处理&#xff08;NLP&#xff09;任务中常用的一种分词方法。 text "Hello, world! This is a test." tokens text.split() print(f"Tok…

第十三章 Java基础-特殊处理

文章目录 1.包和final2.权限修饰符和代码块3.抽象类1.包和final 2.权限修饰符和代码块 3.抽象类

【操作系统原理08】文件管理

文章目录 零.大纲一.文件管理0.大纲1.文件管理1.1 **文件属性**1.2 文件内部数据组织1.3 文件之间的组织1.4操作系统提供功能1.5 文件在外存存放 二.文件的逻辑结构0.大纲1.无结构文件2.有结构文件 三.文件目录0.大纲1.文件控制块2.目录结构3.索引节点(FCB改进) 四.文件共享0.大…

图论学习笔记 5 - 最小树形图

我们不废话&#xff0c;直接进入正题&#xff1a;最小树形图&#xff0c;一个名字看起来很高级的东西。 声明&#xff1a;为了便于理解&#xff0c;可能图片数量会有亿点点多。图片尺寸可能有的较大。 概念 最小树形图的英文是 Directed Minimum Spanning Tree。 相信懂英文…

《自动驾驶轨迹规划实战:Lattice Planner实现避障路径生成(附可运行Python代码)》—— 零基础实现基于离散优化的避障路径规划

《自动驾驶轨迹规划实战&#xff1a;Lattice Planner实现避障路径生成&#xff08;附可运行Python代码&#xff09;》 —— 零基础实现基于离散优化的避障路径规划 一、为什么Lattice Planner成为自动驾驶的核心算法&#xff1f; 在自动驾驶的路径规划领域&#xff0c;Lattice…