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

文章目录

  • 一、定义:备忘录模式
  • 二、模拟场景:备忘录模式
  • 三、改善代码:备忘录模式
    • 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 配置活动。也就是即时性的一般不需要存放到库中进行恢复。
    • 另外如果是使用内存方式存放备忘录,需要考虑存储问题,避免造成内存大量消耗。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HikariCP源码修改,使其连接池支持Kerberos认证

HikariCP-4.0.3 修改HikariCP源码,使其连接池支持Kerberos认证 修改后的Hikari源码地址:https://github.com/Raray-chuan/HikariCP-4.0.3 Springboot使用hikari连接池并进行Kerberos认证访问Impala的demo地址:https://github.com/Raray-chuan/springboot-kerberos-hikari-im…

文本标注技术方案(NLP标注工具)

Doccano doccano 是一个面向人类的开源文本注释工具。它为文本分类、序列标记和序列到序列任务提供注释功能。您可以创建用于情感分析、命名实体识别、文本摘要等的标记数据。只需创建一个项目&#xff0c;上传数据&#xff0c;然后开始注释。您可以在数小时内构建数据集。 支持…

【C++深入浅出】类和对象上篇(类的基础、类的模型以及this指针)

目录 一. 前言 二. 面向对象与面向过程 2.1 面向过程 2.2 面向对象 三. 类的基础知识 3.1 类的引入 3.2 类的定义 3.3 成员变量的命名规则 3.4 封装 3.5 类的访问限定符 3.6 类的作用域 3.7 类的实例化 四. 类的对象模型 4.1 类对象的大小 4.2 类对象的存储方式 …

【Java基础】深入理解反射、反射的应用(工厂模式、代理模式)

文章目录 1. Java反射机制是什么&#xff1f;1.2 Java反射例子 2. Java反射机制中获取Class的三种方式及区别&#xff1f;3. Java反射机制的应用场景有哪些&#xff1f;3.1. 优化静态工厂模式&#xff08;解耦&#xff09;3.1.1 优化前&#xff08;工厂类和产品类耦合&#xff…

leetcode316. 去除重复字母(单调栈 - java)

去除重复字母 题目描述单调栈代码演示进阶优化 上期经典 题目描述 难度 - 中等 leetcode316. 去除重复字母 给你一个字符串 s &#xff0c;请你去除字符串中重复的字母&#xff0c;使得每个字母只出现一次。需保证 返回结果的字典序最小&#xff08;要求不能打乱其他字符的相对…

Darshan日志分析

标头 darshan-parser 输出的开头显示了有关作业的总体信息的摘要。还可以使用–perf、–file或–total命令行选项生成其他作业级别摘要信息。 darshan log version&#xff1a;Darshan 日志文件的内部版本号。compression method&#xff1a;压缩方法。exe&#xff1a;生成日志…

【前端】 Layui点击图片实现放大、关闭效果

实现效果&#xff1a;点击图片实现放大&#xff0c;点击空白处关闭效果。下图。 实现逻辑&#xff1a;二维码是使用JQ插件生成的&#xff0c;点击二维码&#xff0c;获取图片路径&#xff0c;通过Layui的弹窗显示放大后的图片。 Html <div id"qrcode" class&quo…

企业数据加密软件——「天锐绿盾」

「天锐绿盾」是一款企业数据加密软件&#xff0c;主要用于防止企业计算机信息被破坏、丢失和泄密。该软件采用文件过滤驱动实现透明加解密&#xff0c;对用户完全透明&#xff0c;不影响用户操作习惯。 PC访问地址&#xff1a; isite.baidu.com/site/wjz012xr/2eae091d-1b97-4…

两个线程并发(乱序)执行:乱箭穿心 std::thread

C自学精简教程 目录(必读) C并发编程入门 目录 在 创建2个线程并执行 创建10个线程并执行 中&#xff0c;我们已经看到了多个线程执行的顺序是没有任何保证的。 他们之间就是各自独立的同时在执行。 下面我们来看看两个线程同时往控制台打印信息&#xff0c;控制台会乱成什…

YOLOv5算法改进(13)— 替换主干网络之PP-LCNet

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。PP-LCNet是一个由百度团队针对Intel-CPU端加速而设计的轻量高性能网络。它是一种基于MKLDNN加速策略的轻量级卷积神经网络&#xff0c;适用于多任务&#xff0c;并具有提高模型准确率的方法。与之前预测速度相近的模型相比…

IDEA自定义模板

IDEA自定义模板 &#xff08;1&#xff09;定义sop模板 ①在Live Templates中增加模板 ②先定义一个模板的组 ③在模板组里新建模板 ④定义模板 Abbreviation:模板的缩略名称Description:模板的描述Template text:模板的代码片段应用范围。比如点击Define。选择如下&…

手机怎么剪视频?分享一些剪辑工具和注意事项

视频剪辑是一种将多个视频片段进行剪切、合并和编辑的技术&#xff0c;它可以帮助我们制作出精彩的视频作品。如今&#xff0c;随着智能手机的普及&#xff0c;我们可以随时随地使用手机进行视频剪辑。本文将为大家介绍一些手机剪辑工具和注意事项&#xff0c;帮助大家更好地进…

独家首发!openEuler 主线集成 LuaJIT RISC-V JIT 技术

RISC-V SIG 预期随主线发布的 openEuler 23.09 创新版本会集成 LuaJIT RISC-V 支持。本次发版将提供带有完整 LuaJIT 支持的 RISC-V 环境并带有相关软件如 openResty 等软件的支持。 随着 RISC-V SIG 主线推动工作的进展&#xff0c;LuaJIT 和相关软件在 RISC-V 架构下的支持也…

Spring Boot中通过maven进行多环境配置

上文 java Spring Boot将不同配置拆分入不同文件管理 中 我们说到了&#xff0c;多环境的多文件区分管理 说到多环境 其实不止我们 Spring Boot有 很多的东西都有 那么 这就有一个问题 如果 spring 和 maven 都配置了环境 而且他们配的不一样 那么 会用谁的呢&#xff1f; 此…

设计模式—外观模式(Facade)

目录 一、什么是外观模式&#xff1f; 二、外观模式具有什么优点吗&#xff1f; 三、外观模式具有什么缺点呢&#xff1f; 四、什么时候使用外观模式&#xff1f; 五、代码展示 ①、股民炒股代码 ②、投资基金代码 ③外观模式 思维导图 一、什么是外观模式&#xff1f;…

基于JavaWeb和mysql实现校园订餐前后台管理系统(源码+数据库)

一、项目简介 本项目是一套基于JavaWeb和mysql实现网上书城前后端管理系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、项目文档、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都…

python conda实践 sanic框架gitee webhook实践

import subprocess import hmac import hashlib import base64 from sanic.response import text from sanic import Blueprint from git import Repo# 路由蓝图 hook_blue Blueprint(hook_blue)hook_blue.route(/hook/kaifa, methods["POST"]) async def kaifa(req…

使用栈检查括号的合法性 C 实现

使用栈检查括号的合法性 思路讲解&#xff1a;首先从数组数组0下标开始&#xff0c;如果是左括号直接无脑压入栈&#xff0c;直到出现右括号开始判断合法与否。遇到右括号分两种情况&#xff0c;第一种是空栈的情况&#xff0c;也就是说我们第一个字符就是右括号&#xff0c;那…

ShardingSphere——弹性伸缩原理

摘要 支持自定义分片算法&#xff0c;减少数据伸缩及迁移时的业务影响&#xff0c;提供一站式的通用弹性伸缩解决方案&#xff0c;是 Apache ShardingSphere 弹性伸缩的主要设计目标。对于使用单数据库运行的系统来说&#xff0c;如何安全简单地将数据迁移至水平分片的数据库上…

shiro550漏洞分析

准备工作 启动该项目 可以看到没有登录时候&#xff0c;cookie中没有rememberme字段 登录时候 当账号密码输入正确时候 登录后存在该字段 shiro特征&#xff1a; 未登陆的情况下&#xff0c;请求包的cookie中没有rememberMe字段&#xff0c;返回包set-Cookie⾥也没有del…