Java设计模式:四、行为型模式-05:备忘录模式

news2024/12/24 2:08:20

文章目录

  • 一、定义:备忘录模式
  • 二、模拟场景:备忘录模式
  • 三、改善代码:备忘录模式
    • 3.1 工程结构
    • 3.2 备忘录模式模型结构图
    • 3.3 备忘录模式定义
      • 3.3.1 配置信息类
      • 3.3.2 备忘录类
      • 3.3.3 记录者类
      • 3.3.4 管理员类
    • 3.4 单元测试
  • 四、总结:备忘录模式

一、定义:备忘录模式

在这里插入图片描述

  • 备忘录模式:是以可以恢复或者说回滚,配置、版本、悔棋为核心功能的设计模式。
    • 这种设计模式属于行为模式。
    • 在功能实现上是以不破坏原对象为基础增加备忘录操作类,记录原对象的行为从而实现备忘录模式。
  • 备忘录模式的使用场景:后悔药、IDEA 编辑和撤销、小霸王游戏机存档,photoshop

二、模拟场景:备忘录模式

在这里插入图片描述

  • 模拟系统在发布上线的过程中记录线上配置文件用于紧急回滚。
    • 在大型互联网公司系统的发布上线一定是易用、安全、可处理紧急状况的,同时为了可以隔离线上和本地环境,一般会把配置文件抽取出来放到线上,避免有人误操作导致本地的配置内容发布出去。
      • 同时,线上的配置文件也会在每次变更的时候进行记录,包括:版本号、时间、MD5、内容信息和操作人。
    • 在后续上线时如果发现紧急问题,系统就会需要回滚操作,如果执行回滚那么也可以设置配置文件是否回滚。
      • 因为每一个版本的系统可能会带着一些配置文件的信息,这个时候就可以很方便的让系统与配置文件一起回滚操作。
    • 使用备忘录模式,模拟如何记录配置文件信息。

三、改善代码:备忘录模式

  • 备忘录的设计模式实现,重点在于不更改原有类的基础上,增加备忘录类存放记录。

3.1 工程结构

design-step-18
|——src
    |——main
        |--java
            |--com.lino.design
                |--Admin.java
                |--ConfigFile.java
                |--ConfigMemento.java
                |--ConfigOriginator.java
    |--test
        |--java
            |--com.lino.design.test
                |--ApiTest.java

3.2 备忘录模式模型结构图

在这里插入图片描述

  • 以上是工程结构的一个类图,其实相对来说并不复杂,除了原有的配置类 ConfigFile 以外,只新增加了三个类。
    • ConfigMemento:备忘录类,相当于是对原有配置类的扩展。
    • ConfigOriginator:记录者类,获取和返回备忘录类对象信息。
    • Admin:管理员类,用于操作记录备忘信息,比如你一些列的顺序执行了什么或者某个版本下的内容信息。

3.3 备忘录模式定义

3.3.1 配置信息类

ConfigFile.java

package com.lino.design;

import java.util.Date;

/**
 * @description: 配置文件
 */
public class ConfigFile {

    /**
     * 版本号
     */
    private String versionNo;
    /**
     * 内容
     */
    private String content;
    /**
     * 时间
     */
    private Date dateTime;
    /**
     * 操作人
     */
    private String operator;

    public ConfigFile(String versionNo, String content, Date dateTime, String operator) {
        this.versionNo = versionNo;
        this.content = content;
        this.dateTime = dateTime;
        this.operator = operator;
    }

    public String getVersionNo() {
        return versionNo;
    }

    public void setVersionNo(String versionNo) {
        this.versionNo = versionNo;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public Date getDateTime() {
        return dateTime;
    }

    public void setDateTime(Date dateTime) {
        this.dateTime = dateTime;
    }

    public String getOperator() {
        return operator;
    }

    public void setOperator(String operator) {
        this.operator = operator;
    }
}
  • 配置类可以是任何形式的,这里只是简单的描述一个基本的配置内容信息。

3.3.2 备忘录类

ConfigMemento.java

package com.lino.design;

/**
 * @description: 备忘录
 */
public class ConfigMemento {

    private ConfigFile configFile;

    public ConfigMemento(ConfigFile configFile) {
        this.configFile = configFile;
    }

    public ConfigFile getConfigFile() {
        return configFile;
    }

    public void setConfigFile(ConfigFile configFile) {
        this.configFile = configFile;
    }
}
  • 备忘录是对原有配置类的扩展,可以设置和获取配置信息。

3.3.3 记录者类

ConfigOriginator.java

package com.lino.design;

/**
 * @description: 记录者
 */
public class ConfigOriginator {

    private ConfigFile configFile;

    public ConfigFile getConfigFile() {
        return configFile;
    }

    public void setConfigFile(ConfigFile configFile) {
        this.configFile = configFile;
    }

    public ConfigMemento saveMemento() {
        return new ConfigMemento(configFile);
    }

    public void getMemento(ConfigMemento configMemento) {
        this.configFile = configMemento.getConfigFile();
    }
}
  • 记录者类除了对 ConfigFile 配置类增加了获取和设置方法外,还增加了 保存 saveMemento()、获取 getMemento(ConfigMemento configMemento)
    • saveMemento():保存备忘录的时候会创建一个备忘录信息,并返回回去,交给管理者处理。
    • getMemento:获取之后并不是直接返回,而是把备忘录的信息交给现在的配置文件 this.configFile

3.3.4 管理员类

Admin.java

package com.lino.design;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @description: 管理员
 */
public class Admin {

    private int cursorIdx = 0;
    private List<ConfigMemento> mementoList = new ArrayList<>();
    private Map<String, ConfigMemento> mementoMap = new HashMap<>(16);

    public void append(ConfigMemento memento) {
        mementoList.add(memento);
        mementoMap.put(memento.getConfigFile().getVersionNo(), memento);
        cursorIdx++;
    }

    public ConfigMemento undo() {
        if (--cursorIdx <= 0) {
            return mementoList.get(0);
        }
        return mementoList.get(cursorIdx);
    }

    public ConfigMemento redo() {
        if (++cursorIdx > mementoList.size()) {
            return mementoList.get(mementoList.size() - 1);
        }
        return mementoList.get(cursorIdx);
    }

    public ConfigMemento get(String versionNo) {
        return mementoMap.get(versionNo);
    }
}
  • 这个类中主要实现的核心功能就是记录配置文件信息,也就是备忘录的效果,之后提供可以回滚和获取的方法,拿到备忘录的具体内容。
  • 同时这里设置了两个数据结构来存放备忘录,实际使用中可以按需设置。List<ConfigMemento>Map<String, ConfigMemento>
  • 最后是提供的备忘录操作方法。
    • 存放 append()、回滚 undo()、返回 redo()、定向获取 get(String versionNo)

3.4 单元测试

ApiTest.java

package com.lino.design.test;

import com.alibaba.fastjson.JSON;
import com.lino.design.Admin;
import com.lino.design.ConfigFile;
import com.lino.design.ConfigOriginator;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;

/**
 * @description: 单元测试
 */
public class ApiTest {

    private Logger logger = LoggerFactory.getLogger(ApiTest.class);

    @Test
    public void test() {
        Admin admin = new Admin();

        ConfigOriginator configOriginator = new ConfigOriginator();

        configOriginator.setConfigFile(new ConfigFile("100001", "配置内容A=哈哈", new Date(), "小零哥"));
        admin.append(configOriginator.saveMemento());

        configOriginator.setConfigFile(new ConfigFile("100002", "配置内容A=嘻嘻", new Date(), "小零哥"));
        admin.append(configOriginator.saveMemento());

        configOriginator.setConfigFile(new ConfigFile("100003", "配置内容A=默默", new Date(), "小零哥"));
        admin.append(configOriginator.saveMemento());

        configOriginator.setConfigFile(new ConfigFile("100004", "配置内容A=嘿嘿", new Date(), "小零哥"));
        admin.append(configOriginator.saveMemento());

        // 历史配置(回滚)
        configOriginator.getMemento(admin.undo());
        logger.info("历史配置(回滚)undo:{}", JSON.toJSONString(configOriginator.getConfigFile()));

        // 历史配置(回滚)
        configOriginator.getMemento(admin.undo());
        logger.info("历史配置(回滚)undo:{}", JSON.toJSONString(configOriginator.getConfigFile()));

        // 历史配置(前进)
        configOriginator.getMemento(admin.redo());
        logger.info("历史配置(前进)redo:{}", JSON.toJSONString(configOriginator.getConfigFile()));

        // 历史配置(获取)
        configOriginator.getMemento(admin.get("100002"));
        logger.info("历史配置(获取)get:{}", JSON.toJSONString(configOriginator.getConfigFile()));
    }
}
  • 这里包括了四次信息存储和备忘录历史配置操作。
  • 通过上面添加了四次配置后,下面分别进行操作是:
    • 回滚1次再回滚1次之后向前进1次最后是获取指定的版本配置

测试结果

17:04:48.903 [main] INFO  com.lino.design.test.ApiTest - 历史配置(回滚)undo:{"content":"配置内容A=嘿嘿","dateTime":1675415088777,"operator":"小零哥","versionNo":"100004"}
17:04:48.913 [main] INFO  com.lino.design.test.ApiTest - 历史配置(回滚)undo:{"content":"配置内容A=默默","dateTime":1675415088777,"operator":"小零哥","versionNo":"100003"}
17:04:48.913 [main] INFO  com.lino.design.test.ApiTest - 历史配置(前进)redo:{"content":"配置内容A=嘿嘿","dateTime":1675415088777,"operator":"小零哥","versionNo":"100004"}
17:04:48.913 [main] INFO  com.lino.design.test.ApiTest - 历史配置(获取)get:{"content":"配置内容A=嘻嘻","dateTime":1675415088777,"operator":"小零哥","versionNo":"100002"}
  • 从测试结果效果上可以看到,历史配置按照我们的指令进行了回滚和前进,以及最终通过指定的版本进行获取,符合预期结果。

四、总结:备忘录模式

  • 备忘录模式可以满足在不破坏原有属性类的基础上,扩充了备忘录的功能。
  • 在以上的实现中我们将配置模拟存放到内存中,如果关机了会导致配置信息丢失,因为在一些真实的场景里还是需要存放到数据中。
    • 此种存放到内存中进行回复的场景也不是没有,比如:Photoshop、运营人员操作 ERP 配置活动。也就是即时性的一般不需要存放到库中进行恢复。
    • 另外如果是使用内存方式存放备忘录,需要考虑存储问题,避免造成内存大量消耗。

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

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

相关文章

如何构建自己的技术博客

本文相关知识点&#xff1a; markdownVuepress/vitepressGitHub pages 托管服务 为什么建议搭建个人博客网站 拥有自己的技术博客&#xff0c;不仅可以提升自己的技术能力&#xff0c;还可以提升自己的影响力&#xff0c;未来也可能带来一些“睡后”收益。 对于我们职场新人…

QTday2(登录界面+跳转——小黄人篇)

1.完成登录框的按钮操作&#xff0c;并在登录成功后进行界面跳转 form.h&#xff1a; #ifndef FORM_H #define FORM_H#include <QWidget> #include <QPushButton> #include <QDebug> #include <QLineEdit> //行编辑器 #include <QLab…

W5500-EVB-PICO主动PING主机IP检测连通性(十)

前言 上一章我们用W5500_EVB_PICO 开发板做UDP组播数据回环测试&#xff0c;那么本章我们进行W5500_EVB_PICO Ping的测试。 什么是PING&#xff1f; Ping &#xff08;Packet Internet Groper&#xff09;是一种因特网包探索器&#xff0c;用于测试网络连接量的程序 。Ping是…

python读取图像小工具

一、和图像交互获得图像的坐标和像素值 import cv2 import numpy as np import signal import threading import timeif __name__ __main__:img cv2.imread(XXX,0)#读取图片font_face,font_scale,thicknesscv2.FONT_HERSHEY_SIMPLEX,0.5,1#鼠标交互def mouseHandler(event,x…

Android基础之Activity生命周期

Activity是Android四大组件之一、称为之首也恰如其分。 Activity直接翻译为中文叫活动。在Android系统中Activity就是我看到的一个完整的界面。 界面中看到的TextView(文字&#xff09;、Button(按钮)、ImageView&#xff08;图片&#xff09;都是需要Activity来承载的。 总…

linux免密登录最简单--图文详解

最简单的免密登录 1.A电脑生成秘钥 ssh-keygen -t rsa 2.A电脑将秘钥传给B电脑 ssh-copy-id root192.168.1.129 #将秘钥直接传给B电脑 需要输入B电脑的密码&#xff0c;可以看到成功。 3.测试 同理&#xff1a;如果B->A也需要免密登录&#xff0c;统一的操作。 大功告…

集成指挥平台定时任务故障流量分析

01故障现象 集成指挥平台中有定时任务定时传输数据到总队&#xff0c;总队定时下发数据到市交警支队。市交警支队发现定时任务一直出现执行失败的错误。市交警支队和总队联系&#xff0c;说需要市交警支队排查一下自身网络&#xff0c;前两天在应用服务器上面抓了定时任务的数…

大数据、AI和云原生:引领未来软件开发的技术演进

文章目录 **1. 数据驱动的创新&#xff1a;****2. 智能化应用的兴起&#xff1a;****3. 云原生的敏捷和可扩展性&#xff1a;****4. 实时性和即时性&#xff1a;****5. 数据隐私和安全&#xff1a;****6. 跨平台和跨设备&#xff1a;****7. 自动化和智能编程&#xff1a;****8.…

Stable Diffusion 多视图实践

此教程是基于秋叶的webui启动器 1.Stable Diffsuion 使用多视图需要准备一个多角度open pose 图 我给大家提供一个可使用的。 2.需要添加图片到到controlnet当中,不要选择预处理器,选择模型为openpose的模型,然后需要点选同步图片尺寸。 3.然后填写关键字可以参照一下这个…

IBM Spectrum LSF Explorer 为要求苛刻的分布式和任务关键型高性能技术计算环境提供强大的工作负载管理

IBM Spectrum LSF Explorer 适用于 IBM Spectrum LSF 集群的强大、轻量级报告解决方案 亮点 ● 允许不同的业务和技术用户使用单一解决方案快速创建和查看报表和仪表板 ● 利用可扩展的库提供预构建的报告 ● 自定义并生成性能、工作负载和资源使用情况的报…

设计模式-适配器

文章目录 一、简介二、适配器模式基础1. 适配器模式定义与分类2. 适配器模式的作用与优势3.UML图 三、适配器模式实现方式1. 类适配器模式2. 对象适配器模式3.类适配器模式和对象适配器模式对比 四、适配器模式应用场景1. 继承与接口的适配2. 跨平台适配 五、适配器模式与其他设…

无涯教程-Python机器学习 - Extra Trees函数

它是袋装决策树集成方法的另一种扩展。在这种方法中,从训练数据集的样本中构建随机树。 在以下Python食谱中,我们将通过在Pima Indians糖尿病数据集上使用sklearn的ExtraTreesClassifier类来构建额外的树集成模型。 首先,导入所需的软件包,如下所示: from pandas import rea…

Nginx详解 三:高级配置

文章目录 1. 网页的状态页2. Nginx第三方模块2.1 echo模块 3. 变量3.1 内置变量3.1.1 示例 3.2 自定义变量3.2.1 自定义访问日志3.2.2 自定义json 格式日志 3.4 Nginx压缩功能 4. HTTPS4.1 Nginx的HTTPS工作原理4.2 启用功能模块的配置过程 5、自定义图标 1. 网页的状态页 基于…

江苏移动基于OceanBase稳步创新推进核心数据库分布式升级

*本文首发自《中国电信业》 数字经济时代&#xff0c;数据库作为企业核心数据存储、处理、挖潜等方面的关键载体&#xff0c;重要性日益凸显。对于运营商而言&#xff0c;数据库具有行业用户数量多、访问数量多、业务复杂度高、数据安全性高、响应要求性高以及需要 7*24 小时服…

【组合计数 or 树DP】2021 icpc 上海 G

Problem - G - Codeforces 题意&#xff1a; Code&#xff1a; #include <bits/stdc.h>#define int long longusing namespace std;const int mxn1e610; const int mxv1e610; const int mod998244353;vector<int> G[mxn];int N,u,v; int ans1; int sz[mxn];void…

手把手教你写出第一个C语言程序

Hello, World! 1. 前言2. 准备知识2.1 环境2.2 文件的分类2.3 注释2.3.1 注释的作用2.3.2 注释的两种风格2.3.2.1 C语言的注释风格2.3.2.2 C的注释风格 2.3.3 VS中注释和取消注释的快捷键 3. 开始演示3.1 创建项目3.2 创建源文件3.3 写代码3.4 编译链接运行 4. 代码解释4.1 写主…

Apipost:API文档、调试、Mock与测试的一体化协作平台

随着数字化转型的加速&#xff0c;API&#xff08;应用程序接口&#xff09;已经成为企业间沟通和数据交换的关键。而在API开发和管理过程中&#xff0c;API文档、调试、Mock和测试的协作显得尤为重要。Apipost正是这样一款一体化协作平台&#xff0c;旨在解决这些问题&#xf…

QT 使用信号与槽实现界面跳转

一、创建一个新的页面 1 > 在原有工程上新建一个页面 2 > 选择Qt - Qt 设计师界面类 - choose 3 > 选择Widget模板 - 下一步 4 > 输入自定义类名 - 下一步 会自动生成其同名的.h .cpp .ui文件 5 > 最终效果 Headers存放.h文件 Soueces存放.cpp文件 Forms存放.u…

unity 之参数类型之引用类型

文章目录 引用类型引用类型与值类型的差异 引用类型 在Unity中&#xff0c;引用类型是指那些在内存中存储对象引用的数据类型。以下是在Unity中常见的引用类型的介绍&#xff1a; 节点&#xff08;GameObject&#xff09;&#xff1a; 在Unity中&#xff0c;游戏对象&#xff…

学习 使用pandas库 DataFrame 使用

1 、 数据排序 sort_values()函数 by:要排序的名称或名称列表&#xff0c; sorted_df df.sort_values(byAge,ascendingFalse) 由大到小排序&#xff1b; sorted_df df.sort_values(byAge) 由小到大排序&#xff1b; # 创建一个示例数据帧 data {Name: [Tom, Nick, John…