Java二十三种设计模式-备忘录模式(19/23)

news2024/9/21 16:29:16

本文深入探讨了备忘录模式,从定义、组成、实现到使用场景、优缺点、与其他模式的比较,以及最佳实践和替代方案,全面解析了如何在软件开发中有效地保存和恢复对象状态,以支持复杂的撤销操作和历史状态管理。

备忘录模式:保存与恢复对象状态的策略

 基础知识,java设计模式总体来说设计模式分为三大类:

(1)创建型模式,共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

(2)结构型模式,共7种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

(3)行为型模式,共11种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

第一部分:备忘录模式概述

1.1 定义与用途

备忘录模式的基本定义:

备忘录模式是一种软件设计模式:在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。

备忘录模式是一种行为型设计模式,用于在不破坏对象封装性的前提下,捕获并保存对象的当前状态,以便未来可以恢复到该状态。

为何需要备忘录模式:

  • 状态恢复:在需要撤销操作或回滚到之前的状态时,备忘录模式允许对象恢复到特定的历史状态。
  • 封装性维护:通过将状态存储在外部的备忘录对象中,保持了原始对象的封装性,避免了外部直接访问对象的内部状态。

1.2 备忘录模式的组成

发起人(Originator)

  • 定义:负责创建备忘录对象,记录当前时刻的内部状态。
  • 职责:创建一个包含其当前状态的备忘录,并知道何时该状态需要被保存或恢复。

备忘录(Memento)

  • 定义:存储发起人的内部状态,但不允许外部直接访问。
  • 职责:安全地存储状态信息,并允许发起人在需要时恢复这些状态。

负责人(Caretaker)

  • 定义:负责保存和维护备忘录对象,但不干预其内容。
  • 职责:保存备忘录,确保在需要时可以提供给发起人恢复状态,但不修改备忘录中的状态信息。

角色之间的交互

  • 状态保存:发起人在关键时刻创建备忘录,并将其实例传递给负责人进行保存。
  • 状态恢复:当需要恢复状态时,发起人从负责人那里获取备忘录,并使用它来恢复之前的状态。

备忘录模式通过这三种角色的协作,提供了一种机制来捕获和恢复对象的状态,使得用户可以在不同时间点对对象进行快照和回滚。在下一部分中,我们将通过Java代码示例来展示备忘录模式的具体实现。

第二部分:备忘录模式的实现

2.1 Java实现示例

备忘录模式(Memento Pattern)是一种软件设计模式,用于在不破坏封装性的前提下捕获并保存一个对象的内部状态,以便之后可以恢复到该状态。这种模式通常用于实现撤销功能。

以下是Java语言中实现备忘录模式的一个简单示例:

// 发起人角色,负责创建备忘录并使用它来恢复之前的状态
class Originator {
    private String state;

    public void setState(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }

    // 创建一个备忘录,存储当前状态
    public Memento saveStateToMemento() {
        return new Memento(state);
    }

    // 从备忘录中恢复状态
    public void getStateFromMemento(Memento memento) {
        state = memento.getState();
    }
}

// 备忘录角色,负责存储发起人对象的内部状态
class Memento {
    private final String state;

    public Memento(String state) {
        this.state = state;
    }

    public String getState() {
        return state;
    }
}

// 负责人角色,负责对备忘录的创建和存储进行管理
class Caretaker {
    private Memento memento;

    public void setMemento(Memento memento) {
        this.memento = memento;
    }

    public Memento getMemento() {
        return memento;
    }
}

public class MementoDemo {
    public static void main(String[] args) {
        Originator originator = new Originator();
        Caretaker caretaker = new Caretaker();

        originator.setState("State #1");
        caretaker.setMemento(originator.saveStateToMemento());

        originator.setState("State #2");
        System.out.println("Current State: " + originator.getState());

        // 恢复到之前的状态
        originator.getStateFromMemento(caretaker.getMemento());
        System.out.println("Restored State: " + originator.getState());
    }
}

2.2 备忘录模式中的角色和职责

  1. 发起人(Originator)

    • 负责创建一个备忘录,用以记录当前时刻的内部状态。
    • 负责定义一个方法来获取当前状态,以便将其存储到备忘录中。
    • 负责定义一个方法来恢复之前存储的状态。
  2. 备忘录(Memento)

    • 负责存储发起人的内部状态。
    • 通常是一个不可变对象,只能通过发起人来访问。
  3. 负责人(Caretaker)

    • 负责保存好备忘录,不能对备忘录的内容进行操作或检查。
    • 可以决定保存多少个备忘录,以及保存多久。
  4. 客户端(Client)

    • 负责让发起人创建备忘录,并将备忘录传递给负责人。
    • 可以请求发起人恢复到备忘录中的状态,但不会直接与备忘录交互。

备忘录模式的关键在于发起人和备忘录之间的交互,以及负责人对备忘录的保存和管理。通过这种方式,可以安全地保存和恢复对象的状态,而不需要暴露对象的内部实现细节。

第三部分:备忘录模式的使用场景

3.1 需要保存与恢复状态的场景

在许多应用程序中,用户可能希望在进行一系列操作后能够回到之前的状态。以下是一些具体的场景:

  • 文本编辑器:用户在编辑文档时可能会进行多次修改。备忘录模式可以保存文档的每个状态,允许用户在需要时恢复到先前版本。
  • 图形设计软件:在设计过程中,设计师可能会尝试不同的元素布局或颜色方案。通过使用备忘录模式,设计师可以保存每个设计状态,并在不满意时回退到之前的版本。
  • 游戏开发:在游戏开发中,玩家的进度或游戏状态可能需要保存,以便在玩家退出游戏后能够继续之前的游戏进度。
  • 数据库事务:在数据库操作中,事务的回滚和提交可以看作是一种撤销和恢复操作,备忘录模式可以用于实现事务的持久性。

3.2 需要撤销操作的场景

撤销操作是用户界面设计中的一个常见需求,它允许用户撤销他们不希望保留的操作。以下是一些具体的应用场景:

  • 文本编辑:在文本编辑器中,用户可能希望撤销他们刚刚输入的文本或格式更改。备忘录模式可以保存文本的每个状态,以便用户可以逐个撤销操作。
  • 图形用户界面设计:在设计用户界面时,开发者可能希望尝试不同的控件布局或样式。备忘录模式可以保存每个设计步骤,允许开发者逐步撤销更改。
  • 图像编辑:在图像编辑软件中,用户可能希望撤销他们对图像所做的更改,如滤镜应用、颜色调整等。备忘录模式可以保存图像的原始状态和每次更改的状态。
  • 软件开发:在编码过程中,开发者可能会进行多次尝试和错误。使用备忘录模式,可以保存代码的每个版本,允许开发者在必要时撤销到之前的代码状态。

在这些场景中,备忘录模式通过提供一个简单而有效的方式来保存和恢复对象的状态,从而增强了应用程序的灵活性和用户体验。通过这种方式,用户可以更加自信地进行尝试,因为他们知道他们可以随时撤销不想要的操作。

 

第四部分:备忘录模式的优点与缺点

4.1 优点

备忘录模式提供了几个显著的优点,使其成为特定场景下的理想选择:

  1. 状态恢复:这是备忘录模式的主要优势之一。它允许对象在需要时恢复到之前的状态,这对于实现撤销功能至关重要。
  2. 封装性:备忘录模式通过将状态保存在备忘录对象中,而不是直接暴露对象的内部状态,从而保护了对象的封装性。
  3. 灵活性:对象可以在不同的时间点创建多个备忘录,这提供了在不同状态之间选择和切换的灵活性。
  4. 安全性:由于备忘录对象通常只对发起人对象可见,这减少了外部对对象状态的不当访问和修改的风险。
  5. 简化对象接口:对象不需要对外提供复杂的接口来处理状态的保存和恢复,这些操作可以封装在内部。

4.2 缺点

尽管备忘录模式有许多优点,但它也有一些潜在的缺点:

  1. 增加复杂性:实现备忘录模式需要引入额外的类和对象,这可能会使系统的设计和实现变得更加复杂。
  2. 资源消耗:如果系统需要保存大量的状态,每个状态都需要存储相应的备忘录对象,这可能会导致内存消耗显著增加。
  3. 管理负担:负责人(Caretaker)需要管理备忘录对象的生命周期,这可能会增加系统的管理负担,尤其是在需要长期保存大量状态的情况下。
  4. 性能问题:创建和恢复状态可能会涉及到复制对象的状态,这在某些情况下可能会影响性能。
  5. 有限的撤销能力:备忘录模式通常只支持单步撤销,实现多步撤销可能需要更复杂的逻辑和更多的资源消耗。

在使用备忘录模式时,开发者需要权衡这些优缺点,并根据具体的应用场景和需求做出合理的设计决策。例如,在资源受限的环境中,可能需要考虑替代方案或限制状态保存的数量。同时,也可以通过优化备忘录对象的存储和管理来减少资源消耗和提高性能。

 

第五部分:备忘录模式与其他模式的比较

5.1 与命令模式的比较

命令模式备忘录模式都提供了撤销操作的能力,但它们的实现方式和关注点有所不同:

  • 命令模式

    • 将请求封装为对象,从而允许用户对操作进行参数化、队列化和日志记录。
    • 通常包含执行操作的撤销和重做功能。
    • 命令对象知道接收者,即知道需要调用哪个对象的哪个方法。
    • 撤销是通过调用一个撤销方法来实现的,这通常涉及到命令对象内部状态的反转。
  • 备忘录模式

    • 专注于保存和恢复对象的状态,而不是命令或操作。
    • 通过创建一个包含对象状态快照的备忘录来实现撤销。
    • 发起人对象不知道负责人或备忘录的存在,这保持了低耦合性。
    • 撤销是通过恢复到之前保存的状态来实现的。

比较

  • 命令模式更适合需要记录一系列操作并提供撤销和重做功能的场景。
  • 备忘录模式更适合需要保存对象状态以便在将来恢复的场景,特别是当状态恢复不依赖于操作的顺序时。

5.2 与状态模式的对比

状态模式备忘录模式都与对象的状态管理有关,但它们的应用和目的不同:

  • 状态模式

    • 允许一个对象在其内部状态改变时改变其行为,看起来像是改变了其类。
    • 通过状态对象来封装不同的行为,这些状态对象通常是可互换的。
    • 状态模式关注于对象状态的转换,以及在状态改变时如何改变对象的行为。
  • 备忘录模式

    • 用于捕获并保存对象的内部状态,以便可以恢复到该状态。
    • 备忘录模式不关心状态转换的逻辑,只关心状态的保存和恢复。
    • 备忘录模式通常用于实现撤销功能,而不是状态转换。

比较

  • 状态模式更适合于对象的行为随状态变化而变化的场景,例如,一个对象在不同的状态下有不同的行为表现。
  • 备忘录模式更适合于需要保存和恢复对象状态的场景,例如,实现撤销操作或保存游戏进度。

总结来说,虽然命令模式、状态模式和备忘录模式都可以用于管理对象的状态或行为,但它们各自有不同的应用场景和设计目的。开发者在选择设计模式时,应该根据具体的需求和上下文来决定使用哪种模式。

第六部分:备忘录模式的最佳实践和建议

6.1 最佳实践

在使用备忘录模式时,遵循以下最佳实践可以帮助你更有效地实现和利用这一模式:

  1. 合理设计备忘录的存储

    • 只保存必要的状态信息,避免存储整个对象的副本,以减少内存消耗。
    • 考虑使用序列化或压缩技术来减少存储所需的空间。
  2. 确保备忘录的不可变性

    • 一旦备忘录被创建,它的状态就不应该被修改,以确保状态的一致性和可靠性。
    • 通过使用私有构造函数和不可变的数据结构来实现备忘录的不可变性。
  3. 限制备忘录的数量

    • 为避免资源过度消耗,限制可以创建的备忘录数量,例如,只保存最近的N个状态。
  4. 使用合适的数据结构

    • 使用栈或队列等数据结构来管理备忘录对象,以便实现撤销和重做操作。
  5. 清晰的接口设计

    • 为发起人、备忘录和负责人提供清晰和简洁的接口,以简化使用和维护。
  6. 考虑线程安全

    • 如果你的应用程序是多线程的,确保备忘录模式的实现是线程安全的。

6.2 避免滥用

  1. 评估需求

    • 在决定使用备忘录模式之前,评估是否真的需要撤销功能,以避免不必要的复杂性。
  2. 避免过度保存状态

    • 保存过多的状态可能会导致性能下降和资源浪费,因此应该根据实际需求来决定保存状态的频率和数量。
  3. 避免在简单场景中使用

    • 对于不需要复杂状态管理的简单操作,使用备忘录模式可能会过度设计。
  4. 监控性能影响

    • 定期检查备忘录模式对应用程序性能的影响,并在必要时进行优化。

6.3 替代方案

  1. 版本控制系统

    • 对于复杂的状态管理需求,考虑使用版本控制系统,它提供了更强大的状态跟踪和恢复能力。
  2. 栈或队列

    • 对于简单的撤销操作,可以使用栈来保存操作历史,实现后进先出(LIFO)的撤销机制。
  3. 命令模式

    • 如果撤销操作需要记录操作的详细信息,考虑使用命令模式来实现。
  4. 状态模式

    • 如果对象的行为随状态变化而变化,并且状态转换逻辑复杂,可以考虑使用状态模式。
  5. 数据库事务

    • 对于需要持久化状态的场景,可以使用数据库事务来管理状态的保存和恢复。
  6. 事件溯源

    • 对于需要详细历史记录的系统,事件溯源模式可以记录所有状态变化,允许重放和撤销操作。

通过考虑这些最佳实践和替代方案,你可以更明智地决定何时以及如何使用备忘录模式,以及如何优化你的设计以满足特定需求。

结语

备忘录模式提供了一种有效的方式来保存和恢复对象的状态,特别适用于需要撤销操作的场景。通过本文的深入分析,希望读者能够对备忘录模式有更全面的理解,并在实际开发中做出合理的设计选择。


博主还写了其他Java设计模式关联文章,请各位大佬批评指正:

(一)创建型模式(5种):

Java二十三种设计模式-单例模式(1/23)

Java二十三种设计模式-工厂方法模式(2/23)

Java二十三种设计模式-抽象工厂模式(3/23)

Java二十三种设计模式-建造者模式(4/23)

Java二十三种设计模式-原型模式(5/23)

(二)结构型模式(7种): 

Java二十三种设计模式-适配器模式(6/23)

Java二十三种设计模式-装饰器模式(7/23)

Java二十三种设计模式-代理模式(8/23)

Java二十三种设计模式-外观模式(9/23)

Java二十三种设计模式-桥接模式(10/23)

Java二十三种设计模式-组合模式(11/23)

Java二十三种设计模式-享元模式(12/23)

 (三)行为型模式(11种): 

Java二十三种设计模式-策略模式(13/23)

Java二十三种设计模式-模板方法模式(14/23)

Java二十三种设计模式-观察者模式(15/23)

Java二十三种设计模式-迭代子模式(16/23)

Java二十三种设计模式-责任链模式(17/23)

Java二十三种设计模式-命令模式(18/23)

​持续更新中......敬请关注 ​

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2051664.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JAVA OOP】Day09 抽象方法、抽象类、接口、引用类型数组

Day09目标: 理解抽象方法、抽象类的应用场景; 掌握抽象方法、抽象类的语法; 理解接口的应用场景; 掌握接口的语法、应用; 掌握引用类型数组的用法; 代码量:课上120行,课下240行…

AvaloniaChat—从源码构建指南

AvaloniaChat介绍 一个使用大型语言模型进行翻译的简单应用。 我自己的主要使用场景 在看英文文献的过程中,比较喜欢对照着翻译看,因此希望一边是英文一边是中文,虽然某些软件已经自带了翻译功能,但还是喜欢大语言模型的翻译&…

2-69 基于matlab的三坐标雷达目标跟踪数据融合

基于matlab的三坐标雷达目标跟踪数据融合,采用的是概率数据关联算法和EKF,展示了目标的真实轨迹和跟踪滤波轨迹,以及数据融合的轨迹。程序已调通,可直接运行。 2-69 三坐标雷达目标跟踪数据融合 - 小红书 (xiaohongshu.com)

『 Linux 』利用UDP套接字简单进行网络通信

文章目录 Socket常见API转网络字节序网络数据传输的读网络数据传输的写 简单的UDP网络程序服务端基本结构Init() 服务端的初始化Run() 服务端的运行服务端启动及测试 简单的UDP网络程序客户端服务端客户端相互通信测试服务端通过传入命令处理实现远程命令执行参考代码 Socket常…

STM32————串口发送和接收数据包

首先进行实验,对于代码在上一节的基础上,先定义新变量以及增加一个发送数据包函数: 本代码设置FF为包头,FE为包尾,中间为需要传输的数据,一次为4个,之后是接收数据包的函数 当标志位为1代表接收…

SpringBoot+Vue实现大文件上传(分片上传)

SpringBootVue实现大文件上传(分片上传) 1 环境 SpringBoot 3.2.1,Vue 2,ElementUI 2 问题 前几篇文章,可以用于较小文件的上传,对于较大文件来说,为了提高上传效率和可靠性,可以采…

LeetCode题练习与总结:二叉树的右视图--199

一、题目描述 给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。 示例 1: 输入: [1,2,3,null,5,null,4] 输出: [1,3,4]示例 2: 输入: [1,null,3] 输出: [1,3]示例 3: 输入: [] 输出…

判别回形针的方向(clip.hdev)

1.1应用示例思路 (1) 利用Blob分析(一般步骤:图像阈值分割、获取连通区域、计算Blob的相关几何特征),获取目标区域。 (2) 求目标区域的中心坐标和方向。 1.2应用实例代码 * clip.hdev: Orientation of clips*是否必须在活动的图形窗口中显示图标对象…

“论面向服务架构设计及其应用”写作框架,软考高级,系统架构设计师

论文真题 面向服务架构(Service-Oriented Architecture, SOA) 是一种应用框架,将日常的业务应用划分为单独的业务功能服务和流程,通过采用良好定义的接口和标准协议将这些服务关联起来。通过实施基于SOA的系统架构,用…

GPT-5 惊涛来袭:铸就智能新传奇

目录 引言: 正文: 方向一:人工智能发展现状 方向二:GPT-5技术突破预测 方向三:智能系统人类协作 方向四:迎接AI技术变革策略 结束语: 引言: 在科技浩渺浪潮澎湃翻涌的时代&…

ThinkPHP5 5.0.23 远程代码执行漏洞

目录 1、启动环境 2、漏洞利用 3、更改传参方式 4、修改参数 5、发送数据 1、启动环境 docker-compose up -d 2、访问靶机ip端口号8080 2、漏洞利用 使用burpsuite抓包软件抓包 3、更改传参方式 将 GET传参改为POST传参 4、修改参数 url参数 /index.php?scaptcha post参…

聚星文社,绘唐科技AI工具

聚星文社是一个文学创作社区,为广大创作者提供了一个交流和展示作品的平台。绘唐科技是一家AI技术公司,专注于开发人工智能工具和解决方案。绘唐科技与聚星文社合作,为聚星文社的创作者提供了一款AI工具,帮助创作者提升创作效率和…

ES+FileBeat+Kibana日志采集搭建体验

1.环境准备 需要linux操作系统,并安装了docker环境 此处使用虚拟机演示。(虚拟机和docker看参考我之前写的文章) VirtualBox安装Oracle Linux 7.9全流程-CSDN博客 VirtualBox上的Oracle Linux虚拟机安装Docker全流程-CSDN博客 简单演示搭建ES…

高可用集群keepalived 详细讲解

一:高可用集群keepalived LB:Load Balance 负载均衡 负载均衡,其含义就是指将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行,例如FTP服务器、Web服务器、企业核心应用服务器和其它主要任务服务器…

git详细命令

git常用命令,待 1. 前言2. Git安装3. 公钥3.1 生成公钥3.2 配置SSH 密钥 4. 配置用户名和邮箱5. git常用命令5.1 创建仓库命令5.1.1 git init命令5.1.2 git clone[url] 命令 5.2 add 增加 / 删除文件 (跟踪文件)5.3 代码提交5.4 分支5.5 标签…

性能优化理论篇 | Cache VS Buffer,傻傻分不清 ?

性能优化系列目录: 性能优化理论篇 | 彻底弄懂系统平均负载 性能优化理论篇 | swap area是个什么东西 从free命令开始 free 命令是一个在类 Unix 操作系统中用于显示内存使用情况的工具。它的输出包含了系统内存的不同方面,如总内存、已用内存、空闲内存…

不同路径 II[中等]

优质博文:IT-BLOG-CN 一、题目 一个机器人位于一个m x n网格的左上角 (起始点在下图中标记为Start)。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为Finish)。 现在考虑网格中有…

@[TOC](letcode 分类练习 226.翻转二叉树 101. 对称二叉树 104.二叉树的最大深度 111.二叉树的最小深度)

letcode 分类练习 226.翻转二叉树 101. 对称二叉树 104.二叉树的最大深度 111.二叉树的最小深度 226.翻转二叉树101. 对称二叉树104.二叉树的最大深度111.二叉树的最小深度 226.翻转二叉树 利用自底向上的遍历交换左子树和右子树 class Solution { public:TreeNode* invertTr…

后端Web之数据库(以MySQL为例)

目录 1.概述 2.MySQL 3.DDL 4.DML 5.DQL 1.概述 对于我们自己写的一些小功能,数据一般存储在文件中,比如XML文件。而在实际项目中,数据都是存放在数据库中的。数据库(DataBase )是一个存储数据的集合&#xff0c…

双剑合璧,网络无敌!Windows Server 2012 R2双网卡绑定实战教程

文章目录 双剑合璧,网络无敌!Windows Server 2012 R2双网卡绑定实战教程1 背景信息2 配置步骤2.1 登录服务器2.2 分别清除两块网卡的配置2.3 进入“本地服务器”界面2.4 进入“NIC组合”界面2.5 创建网卡绑定组2.6 设置新建组参数2.7 查看已创建的网卡组…