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

一、模式定义

备忘录模式(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<>();
}

一句话总结

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

相关推荐
计算机人哪有不疯的4 分钟前
Hadoop的组成,HDFS架构,YARN架构概述
大数据·数据库·hadoop·spark
林鸿风采5 分钟前
内网服务器之间传输单个大文件最佳解决方案
linux·python·文件传输
森叶5 分钟前
从 JIT 即时编译一直讲到CGI|FastGGI|WSGI|ASGI四种协议的实现细节
python·php·web
文牧之10 分钟前
Oracle 通过 ROWID 批量更新表
运维·数据库·oracle
weixin_3077791312 分钟前
使用FastAPI微服务在AWS EKS中构建上下文增强型AI问答系统
人工智能·python·云计算·fastapi·aws
LLLLLindream15 分钟前
Redis——达人探店
数据库·redis·缓存
一只鹿鹿鹿27 分钟前
智慧能源大数据平台建设方案(PPT)
java·大数据·数据库·能源
时序数据说1 小时前
IoTDB 分段查询语句深度剖析:GROUP BY 与时序语义的完美结合
大数据·数据库·开源·时序数据库·iotdb
AALoveTouch1 小时前
大某麦演唱会门票如何自动抢
python
luo_guibin1 小时前
DVWA在线靶场-SQL注入部分
数据库·sql·mysql