• 【设计模式】行为型设计模式之 备忘录模式(快照模式)


    介绍

    备忘录应用场景明确并且有限,一般用来数据的防丢失、撤销和恢复。对大对象的备份和恢复,备忘录模式能有效的节省时间和空间开销。

    定义

    备忘录模式:也称为快照模式,在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态以便后续能将这个对象恢复成之前的状态。
    备忘录模式的两个要点

    1. 存储副本以便后期恢复
    2. 要在不违反封装原则的前提下恢复

    备忘录模式中的角色

    1. 原发器(Originator):负责创建一个备忘录,用以存储其当前状态;同时也可以利用备忘录恢复其内部状态
    2. 备忘录(Memento):用于存储原发器对象的内部状态。为了保护原发器对象的封装性,备忘录对象通常被设计为不可直接访问其内容,只暴露给原发器。
    3. 负责人(Caretaker):负责保管备忘录对象,但不直接读取或修改备忘录的内容。它只是简单的提供一个存储空间,用来在需要的时候传递给原发器。

    使用记事本程序来理解各个角色

    1. 记事本本身为原发器,可以创建备份、从备份恢复
    2. 记事本备份为 Memento 备忘录,由原发器创建
    3. 保存所有记事本备份的对象则为负责人,保存所有的记事本备份。

    代码示例

    未使用备忘录模式实现输入备份

    /**
     * 简单的文本编辑器
     *
     * @author Jean
     * @date 2024/06/07
     */
    class InputText {
        private StringBuilder text = new StringBuilder();
    
        public String getText() {
            return text.toString();
        }
    
        public void append(String input) {
            text.append(input);
        }
    
        public void setText(String text) {
            this.text.replace(0, this.text.length(), text);
        }
    }
    
    
    /**
     * 保存并处理快照
     *
     * @author Jean
     * @date 2024/06/07
     */
    public class SnapshotHolder {
        private Stack<InputText> snapshots = new Stack<>();
    
        public InputText popSnapshot() {
            return snapshots.pop();
        }
    
        public void pushSnapshot(InputText inputText) {
            InputText deepClonedInputText = new InputText();
            deepClonedInputText.setText(inputText.getText());
            snapshots.push(deepClonedInputText);
        }
    
    
    }
    

    问题:

    1. 为了能尽快恢复快照使用了 setText ,可能导致被其他业务误用,方法违背了封装原则。
    2. 快照本身应该是不可变的,不应该包含任何修改内部状态的函数,但是上述实现直接复用 InputText 的定义,也违反了快照的封装原则。

    使用备忘录模式实现输入备份

        //定义一个文本编辑器,能保存编辑的文本
        //原发器(Originator) 负责创建一个备忘录,用以存储其当前状态;同时也可以利用备忘录恢复其内部状态。
        class InputText{
    
            private StringBuilder text = new StringBuilder();
    
            public String getText(){
                return text.toString();
            }
            public void append(String input){
                text.append(input);
            }
    
        }
    
        //备忘录角色
        //用于存储原发器对象的内部状态。为了保护原发器对象的封装性,备忘录对象通常被设计为不可直接访问其内容,只暴露给原发器。
        class SnapShot{
            private String text;
    
            public SnapShot(String text) {
                this.text = text;
            }
    
            public String getText() {
                return text;
            }
        }
    
        //负责人 caretaker
        //负责保管备忘录对象,但不直接读取或修改备忘录的内容。它只是简单的提供一个存储空间,用来在需要的时候传递给原发器。
        public class SnapshotHolder{
            private Stack<SnapShot> snapShots=new Stack<>();
    
            public SnapShot popSnapshot(){
                return snapShots.pop();
            }
    
            public void PushSnapshot(SnapShot snapShot){
                snapShots.push(snapShot);
            }
    
        }
    

    总结

    备忘录模式适合的场景如下:

    • 当需要实现撤销/重做功能时。
    • 当需要保存和恢复对象的内部状态,但又不想暴露这些状态的细节时。
    • 在某些性能非关键路径上,可以接受因保存状态而带来的资源消耗。

    备忘录模式在实现时,设计者需要权衡状态保存的频率和成本,以确保不会因为过度使用而导致资源紧张。此外,也可以考虑对备忘录进行优化,比如限制保存的状态数量,或者采用更为高效的序列化技术来减少存储开销。

  • 相关阅读:
    简易版剪辑视频程序(python-VideoFileClip)
    盘点6个主流的数据分析工具,及优缺点对比
    阿里云国际站实名认证上传材料填写样例(域名持有者为组织)
    HTTP协议
    BYOL for Audio: Exploring Pre-trainedGeneral-purpose Audio Representations笔记
    开源低代码框架 ReZero API 正式版本发布 ,界面操作直接生成API
    【Go语言刷题篇】Go从0到入门5:Map综合复习、条件语句、循环语句练习
    docker compose 使用
    一棵完全二叉树的第7层(根节点为第0层)有12个叶子节点,求整棵树最多有多少个节点和最少有多少个节点
    Kafka 单机和集群环境部署教程
  • 原文地址:https://blog.csdn.net/weixin_40979518/article/details/139568371