【设计模式-备忘录】

news2024/11/13 18:14:40

备忘录模式(Memento Pattern)是一种行为型设计模式,用于保存对象的内部状态,以便在将来某个时间可以恢复到该状态,而不暴露对象的内部实现细节。备忘录模式特别适合在需要支持撤销(Undo)操作的应用中。

定义

在不破坏封装的前提下,捕获对象的内部状态,并在对象之外保存这个状态,以便日后能将对象恢复到原先保存的状态。

UML图

在这里插入图片描述
备忘录模式涉及的角色:

  • Originator(发起者):负责创建备忘录对象,用来记录自己的内部状态,并可以根据备忘录恢复状态。
  • Memento(备忘录):存储发起者的内部状态,防止对象的内部状态泄露。通常只允许发起者访问其内部状态。
  • Caretaker(负责人):负责保存备忘录对象,但不能对备忘录的内容进行操作或访问。

代码

import java.util.ArrayList;
import java.util.List;

// 发起者类
class Originator {
    private String state;

    // 设置状态
    public void setState(String state) {
        this.state = state;
        System.out.println("当前状态: " + state);
    }

    // 保存当前状态到备忘录
    public Memento saveStateToMemento() {
        return new Memento(state);
    }

    // 从备忘录中恢复状态
    public void getStateFromMemento(Memento memento) {
        state = memento.getState();
        System.out.println("恢复到状态: " + state);
    }

    // 备忘录类
    public static class Memento {
        private final String state;

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

        public String getState() {
            return state;
        }
    }
}

// 负责人类
class Caretaker {
    private final List<Originator.Memento> mementoList = new ArrayList<>();

    // 添加备忘录
    public void add(Originator.Memento state) {
        mementoList.add(state);
    }

    // 获取备忘录
    public Originator.Memento get(int index) {
        return mementoList.get(index);
    }
}

// 测试类
public class MementoPatternDemo {
    public static void main(String[] args) {
        Originator originator = new Originator();
        Caretaker caretaker = new Caretaker();

        // 保存状态
        originator.setState("状态1");
        caretaker.add(originator.saveStateToMemento());

        originator.setState("状态2");
        caretaker.add(originator.saveStateToMemento());

        originator.setState("状态3");
        caretaker.add(originator.saveStateToMemento());

        // 恢复状态
        originator.getStateFromMemento(caretaker.get(1)); // 恢复到状态2
        originator.getStateFromMemento(caretaker.get(0)); // 恢复到状态1
    }
}

场景

1. 撤销/恢复操作

在需要支持“撤销(Undo)”和“恢复(Redo)”的应用中,备忘录模式非常适用。每当状态发生变化时,可以保存一个备忘录对象,用户可以在需要时撤销操作,恢复到之前的状态。
应用场景:

  • 文本编辑器:例如,在文本处理软件中,用户进行编辑时,每次输入、删除或格式化操作都改变文档的状态。通过备忘录模式,用户可以撤销某些操作,并恢复到之前的编辑状态。
  • 绘图软件:绘图过程中每一步的修改都可能需要撤销或恢复。

2. 保存历史状态

当需要记录一个对象的历史状态,并在将来某一时刻恢复特定状态时,备忘录模式是一个合适的选择。这样可以在不破坏对象封装性的前提下保存状态。

应用场景:

  • 版本控制系统:在版本控制中,每次保存都会存储文件或项目的状态,以便之后能够回溯到某个特定版本。
  • 游戏进度保存:在游戏中,玩家的进度(如等级、位置、装备等)可以存储为备忘录对象,并在玩家需要时从存档中恢复游戏。

3. 事务管理

在涉及复杂事务处理的系统中,备忘录模式可以用于事务回滚,即当事务中的某些操作失败时,能够恢复到之前的状态。

应用场景:

  • 数据库事务管理:在处理数据库事务时,如果某一步操作出错,可以回滚到事务开始时的状态,从而保证数据一致性。
  • 金融交易系统:在处理复杂的金融交易时,如果发生异常,能够回滚到交易前的状态,避免数据异常。

4. 需要避免暴露对象细节

在某些情况下,系统可能需要恢复对象的状态,但不希望暴露对象的内部实现细节。备忘录模式可以在不破坏对象封装的情况下,保存和恢复对象状态。

应用场景:

  • 封装复杂对象:例如,某个对象的内部状态比较复杂,外部系统需要在多个状态之间切换,但是不希望外部直接访问对象的内部状态。备忘录模式允许外部保存和恢复状态,而不需要了解对象的内部实现。

5. 定时或阶段性保存

当系统需要阶段性地保存对象的状态,以防系统崩溃或其他意外情况时,备忘录模式可以记录当前的状态,确保系统可以在异常结束后恢复到安全状态。

应用场景:

  • 自动备份系统:例如,某个系统在特定时间或操作后定时保存数据,并在崩溃后自动恢复。

6. 数据快照

当系统需要保存某一时刻的状态快照,以便后续能够对比或恢复时,备忘录模式可以记录状态快照。

应用场景:

  • 调试与监控:开发人员可以在系统运行的不同阶段记录状态快照,并在调试过程中进行恢复以定位问题。

7. 复杂状态管理

对于那些有复杂状态管理需求的系统,备忘录模式可以帮助解决状态的存储与恢复问题,确保状态管理的灵活性。

应用场景:

  • 用户设置的恢复:在软件应用中,用户可能会进行复杂的设置操作,通过备忘录模式,系统可以允许用户将配置恢复到之前的某个设置。

局限性

尽管备忘录模式适用于上述场景,但它在某些情况下可能不是最优选择,特别是当:

  • 状态数据庞大:如果对象的状态信息较大,存储多个备忘录对象可能会占用大量内存。
  • 状态变更频繁:在频繁保存状态的情况下,备忘录的维护成本较高,可能会引发性能问题。

总结

备忘录模式的优点在于它在保持对象封装性、实现撤销功能、简化状态管理、降低耦合度的同时,支持系统的状态历史保存和事务处理回滚。它特别适用于需要保存和恢复对象状态的应用场景。

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

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

相关文章

如何在多台Linux虚拟机上安装和配置Kafka集群

Kafka是一个高性能、分布式的流处理平台&#xff0c;被广泛应用于大规模实时数据处理场景。它具有高吞吐量、低延迟和可水平扩展等特点&#xff0c;能够有效地处理海量数据流。为了提高Kafka的可用性和容错性&#xff0c;通常会在多个节点上部署Kafka集群。在这个指南中&#x…

十大排序(一):冒泡排序

目录 一、简介 实现过程 时间复杂度 二、代码实现 函数声明 Swap函数 单趟 多趟 测试 优化 一、简介 冒泡排序是一种简单的排序算法&#xff0c;它重复地比较相邻的两个元素&#xff0c;如果顺序错误就交换它们&#xff0c;直到没有元素需要交换为止。这个过程类…

获取zabbix API 监控数据shell脚本,自动日常巡检服务器信息、并发送指定群组

一&#xff0c;前言 有zabbix监控&#xff0c;也并不是时刻盯着数据&#xff0c;所以想着&#xff0c;每天固定某个时刻&#xff0c;自动发送服务器数据到指定群组&#xff0c;给其他人更直观的数据。 数据就可以从zabbix API获取。参考官方API文档&#xff1a;https://www.z…

如何使用IIC外设(硬件IIC)

本文重点叙述如何使用芯片自带的 IIC 外设&#xff0c;即硬件 IIC&#xff0c;实现 IIC 通信。文章《IIC通信基础_cpu的iic通信-CSDN博客》 中&#xff0c;叙述了软件 IIC 通信和硬件 IIC 通信的区别&#xff0c; 并且重点叙述了 IIC 的通信协议。在使用软件进行 IIC 通信时&am…

git安装包夸克网盘下载

git安装包夸克网盘下载 git夸克网盘 git网站上的安装包下载速度有点慢&#xff0c;因此为了方便以后下载就将文件保存到夸克网盘上&#xff0c;链接&#xff1a;我用夸克网盘分享了「git」&#xff0c;点击链接即可保存。 链接&#xff1a;https://pan.quark.cn/s/07c73c4a30…

MongoDB在Linux系统中的安装与配置指南

在这篇文章中&#xff0c;我们将介绍如何在CentOS 7服务器上安装MongoDB&#xff0c;并通过DataX将数据从MongoDB迁移到MySQL数据库。这将包括MongoDB的安装、配置、数据准备以及使用DataX进行数据迁移的详细步骤。 MongoDB简介 MongoDB是一个高性能、开源、无模式的文档型数据…

c++9月20日

1.思维导图 2.顺序表 头文件 #ifndef RECTANGLE_H #define RECTANGLE_H#include <iostream>using namespace std;using datatype int ;//类型重定义class Seqlist { private://私有权限datatype *ptr; //指向堆区申请空间的起始地址int size;//堆区空间的长度int len …

鸿蒙OS 线程间通信

鸿蒙OS 线程间通信概述 在开发过程中&#xff0c;开发者经常需要在当前线程中处理下载任务等较为耗时的操作&#xff0c;但是又不希望当前的线程受到阻塞。此时&#xff0c;就可以使用 EventHandler 机制。EventHandler 是 HarmonyOS 用于处理线程间通信的一种机制&#xff0c…

Spring Boot助力高校心理辅导系统升级

3 系统分析 3.1可行性分析 在进行可行性分析时&#xff0c;我们通常根据软件工程里方法&#xff0c;通过四个方面来进行分析&#xff0c;分别是技术、经济、操作和法律可行性。因此&#xff0c;在基于对目标系统的基本调查和研究后&#xff0c;对提出的基本方案进行可行性分析。…

如何着手创建企业数据目录?(三)权限管理及版本控制

前文导读&#xff1a; 《如何着手创建企业数据目录&#xff1f;&#xff08;一&#xff09;数据目录的设定》 《如何着手创建企业数据目录&#xff1f;&#xff08;二&#xff09;数据的命名与维护》 前面聊过了数据目录的设定、数据命名规则和维护规则&#xff0c;今天我们继续…

【java实现json转化为CSV文件】

文章目录 JSON文件中的数据格式测试文件转换的接口 JSON文件中的数据格式 单条数据展开后如下&#xff1a; {"text": "《邪少兵王》是冰火未央写的网络小说连载于旗峰天下","spo_list":[{"predicate": "作者", "objec…

Windows环境下搭建MySQL主从同步实现读写分离

文章目录 前言读写分离的基本原理 环境介绍及说明主库my.ini初始配置创建用于同步的数据库和表 一、新增mysql从库1.复制mysql文件夹2.修改从库的my.ini3.安装到windows服务 二、在my.ini中配置主、从库相关参数1.主库新增配置参数不同版本参数不同问题 2.从库新增配置参数3.删…

rabbitmq 短信验证码

1.生成的验证码用redis存 减少数据库压力 2.通知运营商发送短信的事情交给rabbitmq的队列去做&#xff0c;无论成功或者是失败&#xff0c;用户那边都不知道。没有收到验证码&#xff08;监听失败&#xff09;用户只会觉得是运营商的问题&#xff0c;而不会怀疑是我们的系统有问…

Linux文件IO(四)-返回错误处理与errno详解

1.前言 在上一文章中&#xff0c;给大家编写了很多的示例代码&#xff0c;大家会发现这些示例代码会有一个共同的特点&#xff0c;那就是当判断函数执行失败后&#xff0c;会调用 return 退出程序&#xff0c;但是对于我们来说&#xff0c;我们并不知道为什么会出错&#xff0…

C++进阶|多态知识点详解及经典面试题总结

&#x1f36c; mooridy-CSDN博客 &#x1f9c1;C专栏&#xff08;更新中&#xff01;&#xff09; 目录 1. 多态的概念 2. 多态的定义及实现 2.1 多态的构成条件 2.2 虚函数的重写/覆盖 2.3 虚函数重写的⼀些其他问题 2.4 override 和 final关键字 2.5 重载/重写/隐藏的…

(笔记自用)位运算总结+LeetCode例题:颠倒二进制位+位1的个数

一.位运算总结: 在解题之前理解一下为什么需要位运算&#xff1f;它的本质是什么&#xff1f; 力扣上不少位运算相关的题&#xff0c;并且很多题也会用到位运算的技巧。这又是为什么&#xff1f; 位运算的由来 在计算机里面&#xff0c;任何数据最终都是用数字来表示的&…

mysqldump使用cmd窗口和powersell窗口导出sql中文乱码的问题

项目场景 我在使用Mariadb数据库更新数据的时候&#xff0c;由于数据库的表格中含有中文&#xff0c;在使用mysqldump导出sql语句的时候&#xff0c;中文显示乱码&#xff0c;如下图所示&#xff1a; 环境描述 系统&#xff1a;windows10数据库&#xff1a; Mariadb -10.6.16…

Vue2中路由的介绍和使用

一.路由的基本概念 一说路由&#xff0c;大家首先想到的可能是路由器&#xff0c;路由器的原理就是给连接的设备一个IP地址&#xff0c;通过IP地址来映射到设备&#xff0c;实现连接&#xff0c;本质上是一种映射关系。 在vue2中就是路径与组件间的映射。 路由是使用vue2制作…

Python爬虫使用实例-umei

优美图库 www.umei.cc BV1Ag41137re 1/获取资源 查看网站资源结构 多页&#xff0c;每个item只有一张图 多页&#xff0c;每个item都是一个图集 最大页码 内外层图集均有若干page。 通过尾页按钮确定pageNum&#xff1a; 2/发送请求 response requests.get(urlurl, header…

快速将Mongo Compass的shell语句转成java代码

步骤一、从MongoCompass中获取java代码 将java代码复制出来&#xff0c;从MongoCompass中复制的java代码基本格式如下&#xff1a;&#xff08;是Bson原生格式&#xff09; List<Document> list Arrays.asList(new Document("$match",new Document("name…