「聊设计模式」之备忘录模式(Memento)

2023年 9月 25日 46.6k 0

🏆本文收录于《聊设计模式》专栏,专门攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎持续关注&&收藏&&订阅!

前言

  设计模式是软件设计中经典的解决方案,旨在解决软件开发中常见的问题。备忘录模式(Memento)是其中一种经典的设计模式,它允许在不破坏封装的前提下,保存和恢复对象的内部状态。在本文中,我们将深入了解备忘录模式的实现和应用。

摘要

  备忘录模式是一种行为型设计模式,它能够在不破坏对象封装的情况下,保存和恢复对象的内部状态。在备忘录模式中,我们可以将需要保存的对象状态封装到备忘录对象中,并将备忘录对象存储在一个称为管理者的对象中,以便稍后通过管理者对象恢复状态。

备忘录模式

概念

  备忘录模式是一种行为型设计模式,它允许在不暴露对象实现细节的情况下获取对象状态的全部或部分,并将该状态保存在一个备忘录对象中以便于稍后恢复对象状态。这种模式的核心是备忘录对象,它存储了对象的状态信息,同时提供了对该状态信息的访问和恢复操作。备忘录模式通常用于实现撤销和重做功能,在许多编辑器、文本处理器和图形编辑器等应用程序中都有广泛的应用。

结构

备忘录模式由三个主要组件组成:

  • 发起人(Originator):它是需要备份和恢复状态的对象。它存储它自己的内部状态,并且可以创建和恢复备忘录对象。
  • 备忘录(Memento):它存储了发起人对象的内部状态。尽管备忘录具有发起人的状态信息,但是它无法被除了发起人之外的其他对象访问。
  • 管理者(Caretaker):它负责存储备忘录并提供对备忘录的访问。管理者对象可以存储多个备忘录对象,并可用于恢复先前保存的状态。

如下是备忘录模式的UML类图:

在这里插入图片描述

以下是备忘录模式的实现,我们以一个存储文本编辑器历史记录的例子为例:

//备忘录对象
public class TextEditorMemento {
    private final String text;

    public TextEditorMemento(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }
}
// 发起人对象
public class TextEditor {
    private String text;

    public TextEditor() {
        this.text = "";
    }

    public void setText(String text) {
        this.text = text;
    }

    public TextEditorMemento createMemento() {
        return new TextEditorMemento(text);
    }

    public void restoreMemento(TextEditorMemento memento) {
        this.text = memento.getText();
    }
}
// 管理者对象
public class TextEditorHistory {
    private List history = new ArrayList();

    public void push(TextEditorMemento memento) {
        history.add(memento);
    }

    public TextEditorMemento pop() {
        return history.remove(history.size() - 1);
    }
}

  在这个例子中,TextEditor 是发起人对象,它负责存储文本编辑器的当前文本,并提供方法 createMemento()restoreMemento(TextEditorMemento memento) 来创建备忘录和恢复备忘录。备忘录对象 TextEditorMemento 存储了文本编辑器的文本内容。管理者对象 TextEditorHistory 负责存储备忘录,并提供操作备忘录的方法 push(TextEditorMemento memento)pop()

以下是备忘录模式的应用场景:

  • 当需要保存和恢复对象的一部分或全部状态时。
  • 当直接访问对象状态会导致封装被破坏时。
  • 当需要保存对象的历史快照或可撤销操作历史记录时。

应用场景

  备忘录模式(Memento Pattern)在面向对象设计中被广泛应用,其主要作用是在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,从而可以在需要的时候将对象恢复到先前的状态。

  下面是一些备忘录模式的应用场景:

  • 撤销操作:在许多应用程序中,用户执行操作时需要有“撤销”操作。通过备忘录模式,可以在执行操作前将当前对象的状态保存到备忘录中,当需要撤销时,从备忘录中获取之前保存的状态并恢复对象。

  • 数据库事务回滚:在数据库系统中,事务是一组被视为一个单独工作单元的操作。在执行事务时,如果发生了错误,需要将整个事务回滚到最初的状态。备忘录模式可以用来实现这种回滚,将事务执行前的状态保存到备忘录中,当出现错误时可以从备忘录中恢复原始状态。

  • 游戏存档:在许多游戏中,玩家需要保存游戏进度,以便在之后的时间里恢复到该点。备忘录模式可以用来保存游戏状态,让玩家在需要时回到先前的状态。

  • 操作系统恢复:当操作系统出现错误或崩溃时,需要将系统的状态恢复到某个先前的时间点。备忘录模式可以利用系统快照机制,将系统的状态保存到备忘录中,从而实现系统恢复。

  •   总之,备忘录模式在许多应用程序中都有着广泛的应用,其主要作用是在不破坏封装性的前提下,捕获对象的内部状态,以便将来可以恢复对象到先前的状态。

    优缺点

    优点:

    备忘录模式的优点如下:

  • 分离了数据与操作,使得数据可以独立于程序进行存储和操作,从而保证了程序的稳定性和安全性;
  • 实现了撤销和恢复功能,用户可以回退到之前的版本,避免了操作的不可逆性;
  • 备忘录模式可以在不破坏封装性的前提下捕获一个对象的内部状态,并在需要时回滚该对象到原始状态,比较灵活。
  • 缺点:

    备忘录模式的缺点如下:

  • 当需要备份大量的数据时,备忘录模式会占用较大的内存空间;
  • 实现备忘录模式需要对对象进行额外的存储,增加了程序的复杂度;
  • 由于备忘录模式需要实时存储和恢复对象的状态,可能会影响程序的性能,特别是大规模数据的处理时。
  • 模式实现

    TextEditorMemento

    package com.example.javaDesignPattern.memento;
    
    /**
     * @author bug菌
     * @version 1.0
     * @date 2023/9/20 11:21
     */
    public class TextEditorMemento {
        private final String text;
    
        public TextEditorMemento(String text) {
            this.text = text;
        }
    
        public String getText() {
            return text;
        }
    }
    

      TextEditorMemento 是备忘录对象,它存储了文本编辑器的文本内容。它具有一个字符串类型的 text 属性和方法 getText() 用于获取 text 属性的值。

    TextEditor

    package com.example.javaDesignPattern.memento;
    
    /**
     * @author bug菌
     * @version 1.0
     * @date 2023/9/20 11:22
     */
    public class TextEditor {
        private String text;
    
        public TextEditor() {
            this.text = "";
        }
    
        public void setText(String text) {
            this.text = text;
        }
    
        public String getText() {
            return text;
        }
    
        public TextEditorMemento createMemento() {
            return new TextEditorMemento(text);
        }
    
        public void restoreMemento(TextEditorMemento memento) {
            this.text = memento.getText();
        }
    }
    

      TextEditor 是发起人对象,它负责存储文本编辑器的当前文本,并提供方法 createMemento()restoreMemento(TextEditorMemento memento) 来创建备忘录和恢复备忘录。它具有一个字符串类型的 text 属性和方法 setText(String text) 用于设置 text 属性的值。

    • TextEditor(): 构造方法,初始化 text 为 ""。
    • void setText(String text): 设置 text 属性的值。
    • TextEditorMemento createMemento(): 创建备忘录对象并返回,它将 text 属性存储在备忘录中。
    • void restoreMemento(TextEditorMemento memento): 从备忘录中恢复 text 属性的值。

    TextEditorHistory

    package com.example.javaDesignPattern.memento;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author bug菌
     * @version 1.0
     * @date 2023/9/20 11:22
     */
    public class TextEditorHistory {
        private List history = new ArrayList();
    
        public void push(TextEditorMemento memento) {
            history.add(memento);
        }
    
        public TextEditorMemento pop() {
            return history.remove(history.size() - 1);
        }
    }
    

      TextEditorHistory 是管理者对象,它负责存储备忘录,并提供操作备忘录的方法 push(TextEditorMemento memento)pop()。它具有一个 history 属性,它用于存储备忘录对象列表。

    • void push(TextEditorMemento memento): 将备忘录添加到 history 中。
    • TextEditorMemento pop(): 从 history 中删除并返回最近添加的备忘录。

    测试用例

    public class MementoPatternTest {
    
        @Test
        public void testMementoPattern() {
            TextEditor editor = new TextEditor();
            TextEditorHistory history = new TextEditorHistory();
    
            editor.setText("Hello World!");
            history.push(editor.createMemento());
    
            editor.setText("Goodbye World!");
            history.push(editor.createMemento());
    
            editor.restoreMemento(history.pop());
            assertEquals(editor.getText(), "Goodbye World!");
    
            editor.restoreMemento(history.pop());
            assertEquals(editor.getText(), "Hello World!");
        }
    }
    

    执行结果如下所示:

    在这里插入图片描述

      在这个测试用例中,我们首先创建了一个文本编辑器对象和一个历史记录对象。接下来,我们设置文本编辑器的文本为Hello World!,创建备忘录,并将备忘录添加到历史记录中。然后我们更新文本编辑器的文本为 Goodbye World!,创建备忘录,并将备忘录添加到历史记录中。最后,我们将文本编辑器恢复到先前的状态,并验证其文本是否正确。

    测试用例代码解析

      这段代码是一个单元测试,在使用Memento设计模式实现的TextEditor类中,测试了从历史记录(TextEditorHistory)中恢复(TextEditor.restoreMemento)已保存的文本(TextEditor.createMemento)的功能。

    具体来说,测试分为以下步骤:

    • 创建TextEditor实例和TextEditorHistory实例;
    • 调用TextEditor.setText方法,设置文本为"Hello World!",并通过TextEditor.createMemento方法创建一个Memento对象,将其推入历史记录中;
    • 再次调用TextEditor.setText方法,设置文本为Goodbye World!,并将其推入历史记录中;
    • 调用历史记录的TextEditorHistory.pop方法获取最后一个保存的Memento对象,并通过TextEditor.restoreMemento方法将其恢复为之前存储的状态,即Goodbye World!
    • 再次调用历史记录的TextEditorHistory.pop方法获取上一个保存的Memento对象,并通过TextEditor.restoreMemento方法将其恢复为之前存储的状态,即Hello World!
    • 使用JUnitassertEquals方法来比较恢复后的文本是否与预期相同。

      这个测试用例确保了Memento模式在TextEditor类中正确地实现。

    小结

      备忘录模式是一种行为型设计模式,它允许在不破坏对象封装的情况下,保存和恢复对象的内部状态。在备忘录模式中,我们可以将需要保存的对象状态封装到备忘录对象中,并将备忘录对象存储在一个称为管理者的对象中,以便稍后通过管理者对象恢复状态。

      备忘录模式的主要组件包括发起人对象、备忘录对象和管理者对象。发起人对象是需要备份和恢复状态的对象,它存储它自己的内部状态,并且可以创建和恢复备忘录对象。备忘录对象存储了发起人对象的内部状态。

    附录源码

      如上涉及代码均已上传同步在GitHub,提供给同学们参考性学习。

    总结

      备忘录模式是一种行为型设计模式,其核心在于在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,从而可以在需要的时候将对象恢复到先前的状态。备忘录模式由发起人、备忘录和管理者三种角色组成,其中发起人是需要备份和恢复状态的对象;备忘录是存储发起人对象内部状态的对象;管理者负责存储备忘录并提供对备忘录的访问。

      备忘录模式的优点在于分离了数据与操作,实现了撤销和恢复功能以及比较灵活;缺点在于可能会占用较大的内存空间,增加了程序的复杂度,以及可能会影响程序的性能。备忘录模式应用广泛,例如实现撤销操作、数据库事务回滚、游戏存档以及操作系统恢复等。

    ☀️建议/推荐你

      如果想系统性的全面学习设计模式,建议小伙伴们直接毫无顾忌的关注这个专栏《聊设计模式》,无论你是想提升自己的编程技术,还是渴望更好地理解代码背后的设计思想,本专栏都会为你提供实用的知识和启发,帮助你更好地解决日常开发中的挑战,将代码变得更加优雅、灵活和可维护!

      最后,如果这篇文章对你有所帮助,帮忙给作者来个一键三连,关注、点赞、收藏,您的支持就是我坚持写作最大的动力。

      同时欢迎大家关注公众号:「猿圈奇妙屋」 ,以便学习更多同类型的技术文章,免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板、技术文章Markdown文档等海量资料。

    📣关于我

      我是bug菌,CSDN | 掘金 | infoQ | 51CTO 等社区博客专家,历届博客之星Top30,掘金年度人气作者Top40,51CTO年度博主Top12,华为云 | 阿里云| 腾讯云等社区优质创作者,全网粉丝合计15w+ ;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板等海量资料。

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论