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

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


前言

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

摘要

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

备忘录模式

概念

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

结构

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

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

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

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

java 复制代码
//备忘录对象
public class TextEditorMemento {
    private final String text;

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

    public String getText() {
        return text;
    }
}
java 复制代码
// 发起人对象
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();
    }
}
java 复制代码
// 管理者对象
public class TextEditorHistory {
    private List<TextEditorMemento> 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)在面向对象设计中被广泛应用,其主要作用是在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,从而可以在需要的时候将对象恢复到先前的状态。

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

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

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

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

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

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

优缺点

优点:

备忘录模式的优点如下:

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

缺点:

备忘录模式的缺点如下:

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

模式实现

TextEditorMemento

java 复制代码
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

java 复制代码
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

java 复制代码
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<TextEditorMemento> 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 中删除并返回最近添加的备忘录。

测试用例

java 复制代码
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电子书籍、简历模板等海量资料。


相关推荐
蜗牛^^O^41 分钟前
Docker和K8S
java·docker·kubernetes
从心归零1 小时前
sshj使用代理连接服务器
java·服务器·sshj
IT毕设梦工厂2 小时前
计算机毕业设计选题推荐-在线拍卖系统-Java/Python项目实战
java·spring boot·python·django·毕业设计·源码·课程设计
Ylucius3 小时前
动态语言? 静态语言? ------区别何在?java,js,c,c++,python分给是静态or动态语言?
java·c语言·javascript·c++·python·学习
凡人的AI工具箱3 小时前
AI教你学Python 第11天 : 局部变量与全局变量
开发语言·人工智能·后端·python
是店小二呀3 小时前
【C++】C++ STL探索:Priority Queue与仿函数的深入解析
开发语言·c++·后端
七夜zippoe3 小时前
分布式系统实战经验
java·分布式
canonical_entropy3 小时前
金蝶云苍穹的Extension与Nop平台的Delta的区别
后端·低代码·架构
是梦终空3 小时前
JAVA毕业设计176—基于Java+Springboot+vue3的交通旅游订票管理系统(源代码+数据库)
java·spring boot·vue·毕业设计·课程设计·源代码·交通订票
落落落sss3 小时前
sharding-jdbc分库分表
android·java·开发语言·数据库·servlet·oracle