备忘录模式深度解析与实战案例

一、模式定义

备忘录模式(Memento Pattern) 是一种行为设计模式,用于在不破坏对象封装性的前提下,捕获并保存对象的内部状态,以便后续恢复。核心思想是 状态快照管理,常用于实现撤销/重做、事务回滚等功能。

二、核心组件

组件 作用
Originator 原始对象,产生状态快照
Memento 备忘录对象,存储Originator的状态
Caretaker 状态管理器,保存和管理备忘录历史

三、模式优势

状态封装:不暴露对象内部实现细节

历史管理:支持多级撤销/重做操作

时间回溯:实现任意时间点的状态恢复

职责分离:状态存储与业务逻辑解耦

四、真实场景案例:文档版本控制系统

场景需求

开发IDE需要实现:

文档编辑时自动保存历史版本

支持最多50个历史版本的存储

允许用户自由切换文档版本

版本元数据记录(时间、作者)

五、Java实现代码

1. 备忘录接口(扩展元数据支持)

java 复制代码
public interface DocumentMemento {
    String getVersionId();
    LocalDateTime getCreateTime();
    String getAuthor();
}

2. 原发器(文档对象)

java 复制代码
public class Document {
    private StringBuilder content = new StringBuilder();
    private String currentAuthor;
    
    public Document(String author) {
        this.currentAuthor = author;
    }

    // 编辑文档
    public void append(String text) {
        content.append(text);
    }

    // 创建快照
    public DocumentSnapshot createSnapshot() {
        return new DocumentSnapshot(
            UUID.randomUUID().toString(),
            LocalDateTime.now(),
            currentAuthor,
            content.toString()
        );
    }

    // 恢复快照
    public void restore(DocumentMemento memento) {
        if (memento instanceof DocumentSnapshot) {
            DocumentSnapshot snapshot = (DocumentSnapshot) memento;
            this.content = new StringBuilder(snapshot.getContent());
            this.currentAuthor = snapshot.getAuthor();
        }
    }

    // 内部备忘录实现
    private static class DocumentSnapshot implements DocumentMemento {
        private final String versionId;
        private final LocalDateTime createTime;
        private final String author;
        private final String content;

        public DocumentSnapshot(String versionId, LocalDateTime createTime, 
                               String author, String content) {
            this.versionId = versionId;
            this.createTime = createTime;
            this.author = author;
            this.content = content;
        }

        // Getters实现...
    }
}

3. 负责人(版本控制器)

java 复制代码
public class VersionController {
    private final Deque<DocumentMemento> history = new ArrayDeque<>();
    private final Deque<DocumentMemento> redoStack = new ArrayDeque<>();
    private static final int MAX_HISTORY = 50;

    public void saveVersion(DocumentMemento memento) {
        if (history.size() >= MAX_HISTORY) {
            history.removeFirst();
        }
        history.push(memento);
        redoStack.clear(); // 新操作后清空重做栈
    }

    public DocumentMemento undo() {
        if (!history.isEmpty()) {
            DocumentMemento current = history.pop();
            redoStack.push(current);
            return history.peekFirst(); // 返回前一个版本
        }
        return null;
    }

    public DocumentMemento redo() {
        if (!redoStack.isEmpty()) {
            DocumentMemento redo = redoStack.pop();
            history.push(redo);
            return redo;
        }
        return null;
    }

    public List<DocumentMemento> getVersionHistory() {
        return new ArrayList<>(history);
    }
}

4. 客户端使用

java 复制代码
public class IDEApplication {
    public static void main(String[] args) {
        // 初始化文档和版本控制器
        Document doc = new Document("DeveloperA");
        VersionController controller = new VersionController();

        // 首次保存
        doc.append("初始项目结构\n");
        controller.saveVersion(doc.createSnapshot());

        // 编辑并保存版本
        doc.append("添加用户模块\n");
        controller.saveVersion(doc.createSnapshot());
        
        doc.append("实现登录功能\n");
        controller.saveVersion(doc.createSnapshot());

        // 显示历史版本
        System.out.println("=== 版本历史 ===");
        controller.getVersionHistory().forEach(m -> 
            System.out.printf("[%s] %s - %s%n", 
                m.getVersionId().substring(0,8),
                m.getCreateTime().format(DateTimeFormatter.ISO_LOCAL_TIME),
                m.getAuthor())
        );

        // 执行撤销
        System.out.println("\n=== 执行撤销 ===");
        DocumentMemento undoVersion = controller.undo();
        doc.restore(undoVersion);
        System.out.println("当前内容:\n" + doc.getContent());

        // 执行重做
        System.out.println("\n=== 执行重做 ===");
        DocumentMemento redoVersion = controller.redo();
        doc.restore(redoVersion);
        System.out.println("当前内容:\n" + doc.getContent());
    }
}

六、运行结果

text 复制代码
=== 版本历史 ===
[3b7f8a9c] 14:25:36.123 - DeveloperA
[2a6e5d8f] 14:25:35.987 - DeveloperA
[1c4d3b2a] 14:25:35.845 - DeveloperA

=== 执行撤销 ===
当前内容:
初始项目结构
添加用户模块

=== 执行重做 ===
当前内容:
初始项目结构
添加用户模块
实现登录功能

七、模式变体与优化

1. 增量快照

java 复制代码
// 差异存储优化
public class DeltaMemento implements DocumentMemento {
    private final String diff;  // 差异内容
    private final PatchType type; // 变更类型

    public enum PatchType { INSERT, DELETE, REPLACE }
}

2. 加密快照

java 复制代码
// 安全增强
public class SecureMemento implements DocumentMemento {
    private byte[] encryptedData;
    
    public SecureMemento(String content) {
        this.encryptedData = AESUtil.encrypt(content);
    }
    
    public String getDecryptedContent(String key) {
        return AESUtil.decrypt(encryptedData, key);
    }
}

八、行业应用场景

场景 具体应用 优势体现
图形编辑器 绘图步骤撤销/重做 支持复杂操作回溯
游戏存档 玩家进度保存与加载 实现任意时刻存档恢复
数据库事务 事务回滚机制 保证数据一致性
配置管理系统 配置变更历史追踪 快速回滚错误配置
浏览器历史记录 页面导航前进/后退 优化用户体验

九、最佳实践建议

快照频率控制

java 复制代码
// 自动保存策略
public enum SavePolicy {
    ON_CHANGE,  // 每次变更保存
    TIMED,      // 定时保存
    MANUAL      // 手动保存
}

大对象处理

java 复制代码
// 使用序列化存储快照
public class SerializedMemento implements Serializable {
    private byte[] serializedData;
    
    public SerializedMemento(Originator originator) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(originator);
        this.serializedData = bos.toByteArray();
    }
}

内存优化

java 复制代码
// 软引用缓存
public class MemoryOptimizedCaretaker {
    private List<SoftReference<DocumentMemento>> history = new ArrayList<>();
}

一句话总结

备忘录模式主要解决的是对象状态的保存和恢复问题,通过创建备忘录对象来存储和恢复对象的内部状态。

相关推荐
Python图像识别12 小时前
71_基于深度学习的布料瑕疵检测识别系统(yolo11、yolov8、yolov5+UI界面+Python项目源码+模型+标注好的数据集)
python·深度学习·yolo
千码君201613 小时前
React Native:从react的解构看编程众多语言中的解构
java·javascript·python·react native·react.js·解包·解构
淮北49414 小时前
windows安装minicoda
windows·python·conda
TDengine (老段)15 小时前
TDengine 数学函数 DEGRESS 用户手册
大数据·数据库·sql·物联网·时序数据库·iot·tdengine
TDengine (老段)15 小时前
TDengine 数学函数 GREATEST 用户手册
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
安当加密15 小时前
云原生时代的数据库字段加密:在微服务与 Kubernetes 中实现合规与敏捷的统一
数据库·微服务·云原生
爱喝白开水a15 小时前
LangChain 基础系列之 Prompt 工程详解:从设计原理到实战模板_langchain prompt
开发语言·数据库·人工智能·python·langchain·prompt·知识图谱
想ai抽15 小时前
深入starrocks-多列联合统计一致性探查与策略(YY一下)
java·数据库·数据仓库
武子康15 小时前
Java-152 深入浅出 MongoDB 索引详解 从 MongoDB B-树 到 MySQL B+树 索引机制、数据结构与应用场景的全面对比分析
java·开发语言·数据库·sql·mongodb·性能优化·nosql
longgyy16 小时前
5 分钟用火山引擎 DeepSeek 调用大模型生成小红书文案
java·数据库·火山引擎