[设计模式Java实现附plantuml源码~行为型] 撤销功能的实现——备忘录模式

前言:
为什么之前写过Golang 版的设计模式,还在重新写Java 版?
答:因为对于我而言,当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言,更适合用于学习设计模式。
为什么类图要附上uml
因为很多人学习有做笔记的习惯,如果单纯的只是放一张图片,那么学习者也只能复制一张图片,可复用性较低,附上uml,方便有新理解时,快速出新图。


🔥[设计模式Java实现附plantuml源码]专链

  • 创建型
  1. 确保对象的唯一性~单例模式
  2. 集中式工厂的实现~简单工厂模式
  3. 多态工厂的实现——工厂方法模式
  4. 产品族的创建——抽象工厂模式
  5. 对象的克隆~原型模式
  6. 复杂对象的组装与创建——建造者模式
  • 结构型
  1. 提供统一入口——外观模式
  2. 扩展系统功能——装饰模式
  3. 树形结构的处理——组合模式
  4. 对象的间接访问——代理模式
  5. 不兼容结构的协调——适配器模式
  6. 处理多维度变化——桥接模式
  7. 实现对象的复用——享元模式
  • 行为型
  1. 请求的链式处理——职责链模式

  2. 请求发送者与接收者解耦——命令模式

  3. 遍历聚合对象中的元素——迭代器模式

  4. 协调多个对象之间的交互——中介者模式

  5. 对象间的联动~观察者模式

  6. 撤销功能的实现——备忘录模式


备忘录模式提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤。当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。当前很多软件都提供了撤销(Undo)操作,其中就使用了备忘录模式。

备忘录模式定义如下:备忘录模式(Memento Pattern):在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存的状态。它是一种对象行为型模式,其别名为Token。

备忘录模式的核心是备忘录类以及用于管理备忘录的负责人类的设计,其结构如图所示。
在这里插入图片描述

@startuml

class Memento {
- state
+ gettersetter()
}

class Caretaker {

}

class Originator {
- state
+ createMemento()
+ restoreMemento(Memento memento)
}
note left of Originator::createMemento
return new Memento;
end note

note left of Originator::restoreMemento
memento.getState()
end note

Originator ..right.>Memento
Caretaker *-up-> Memento: memento

@enduml

由图可以看出,在备忘录模式结构图中包含以下3个角色。
(1)Originator(原发器):它是一个普通类,可以创建一个备忘录,并存储其当前内部状态,也可以使用备忘录来恢复其内部状态。一般将需要保存内部状态的类设计为原发器。
(2)Memento(备忘录):存储原发器的内部状态,根据原发器来决定保存哪些内部状态。备忘录的设计一般可以参考原发器的设计,根据实际需要确定备忘录类中的属性。需要注意的是,除了原发器本身与负责人类之外,备忘录对象不能直接供其他类使用。原发器的设计在不同的编程语言中实现机制会有所不同。
(3)Caretaker(负责人):负责人又称为管理者,他负责保存备忘录,但是不能对备忘录的内容进行操作或检查。在负责人类中可以存储一个或多个备忘录对象,他只负责存储对象,而不能修改对象,也无须知道对象的实现细节。

理解备忘录模式并不难,但关键在于如何设计备忘录类和负责人类。由于在备忘录中存储的是原发器的中间状态,因此需要防止原发器以外的其他对象访问备忘录,特别是不允许其他对象来修改备忘录。

简单代码实现

package behavior;

import java.util.Stack;

public class MementoDemo {

    // Memento class
    public record Memento(String state){}

    // Caretaker class
    public static class Caretaker {
        private final Stack<Memento> mementoStack = new Stack<>();

        public void saveMemento(Memento memento) {
            mementoStack.push(memento);
        }

        public Memento restoreMemento() {
            return mementoStack.pop();
        }
    }

    // Originator class
    public static class Originator {
        private String state;

        public Memento createMemento() {
            return new Memento(state);
        }

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

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

        public String getState() {
            return state;
        }
    }

    // Client code

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

            originator.setState("s1");
            System.out.println("1 " + originator.getState());

            caretaker.saveMemento(originator.createMemento());

            originator.setState("s2");
            System.out.println("2 " + originator.getState());
            caretaker.saveMemento(originator.createMemento());

            originator.setState("s3");
            System.out.println("3 " + originator.getState());

            originator.restoreMemento(caretaker.restoreMemento());
            System.out.println("4 " + originator.getState());
            originator.restoreMemento(caretaker.restoreMemento());
            System.out.println("5 " + originator.getState());
        }

}

结果

1 s1
2 s2
3 s3
4 s2
5 s1

总结

备忘录模式在很多软件的使用过程中普遍存在,但是在应用软件开发中,它的使用频率并不太高,因为现在很多基于窗体和浏览器的应用软件并没有提供撤销操作。如果需要为软件提供撤销功能,备忘录模式无疑是一种很好的解决方案。在一些字处理软件、图像编辑软件、数据库管理系统等软件中备忘录模式都得到了很好的应用。

主要优点

备忘录模式的主要优点如下:
(1)它提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤。当新的状态无效或者存在问题时,可以使用暂时存储起来的备忘录将状态复原。
(2)备忘录实现了对信息的封装。一个备忘录对象是一种原发器对象状态的表示,不会被其他代码所改动。备忘录保存了原发器的状态,采用列表、堆栈等集合来存储备忘录对象可以实现多次撤销操作。

主要缺点

备忘录模式的主要缺点是:资源消耗过大。如果需要保存的原发器类的成员变量太多,就不可避免地需要占用大量的存储空间,每保存一次对象的状态都需要消耗一定的系统资源。

适用场景

在以下情况下可以考虑使用备忘录模式:
(1)保存一个对象在某一个时刻的全部状态或部分状态,这样以后需要时就能够恢复到先前的状态,实现撤销操作。
(2)防止外界对象破坏一个对象历史状态的封装性,避免将对象历史状态的实现细节暴露给外界对象。


🚀 作者简介:作为某云服务提供商的后端开发人员,我将在这里与大家简要分享一些实用的开发小技巧。在我的职业生涯中积累了丰富的经验,希望能通过这个博客与大家交流、学习和成长。技术栈:Java、Golang、PHP、Python、Vue、React


本文收录于三木的
💐 「设计模式」专栏
此外三木还有以下专栏在同步更新~

🌼 「AI」专栏

🔥「面试」这个专栏的灵感来自于许多粉丝私信,大家向我咨询有关面试的问题和建议。我深感荣幸和责任,希望通过这个专栏,能够为大家提供更多关于面试的知识、技巧和经验。我们将一起探讨面试。期待粉丝们ssp的offer喜讯。

🎈 「Java探索者之路」系列专栏,这个专栏旨在引领Java开发者踏上一段真正探索Java世界的旅程。
我们将深入探讨Java编程的方方面面,从基础知识到高级技巧,从实践案例到最新趋势,帮助你成为一名卓越的Java探索者。如果有想进入Java后端领域工作的同学,这个专栏会对你有所帮助,欢迎关注起来呀

🌊 「Python爬虫」的入门学习系列,大家有兴趣的可以看一看


🌹一起学习,互三互访,顺评论区有访必回,有关必回!!!


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

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

相关文章

formality:set_constant应用

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 往期文章链接: formality:形式验证流程 scan mode func的功能检查需要把scan mode设置成0。

python Matplotlib Tkinter-->导出pdf报表

环境 python:python-3.12.0-amd64 包: matplotlib 3.8.2 reportlab 4.0.9 import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk import tkinter as tk import tkinter.messagebox as messagebox impor…

逆变器专题(9)-光伏并网逆变器PQ控制

相应仿真原件请移步资源下载 原理 跟网型并网逆变器通常采用PQ功率环并网&#xff0c;采用功率环并网通常可以进行设计其功率因数 如图所示&#xff0c;通过控制有功功率、无功功率的值&#xff0c;即可实现对功率因数的控制&#xff0c;通常情况下在并网时可将无功功率控为0&…

虚拟机JVM

虚拟机 1、定义jvm 假想计算机 运行在操作系统之上 和硬件之间没有直接交互 包括 一套字节码指令、寄存器、栈、垃圾回收、堆 一个存储方法域 jvm:承担一个翻译工作&#xff0c;动态的将java代码编译成操作系统可以识别的机器码。 从软件层面屏蔽了不同操作系统在底层硬件与指…

js实现鼠标拖拽改变div大小的同时另一个div宽度也变化

实现效果如下图所示 源码如下 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style>.box {width: 100%;height: 300px; display: flex;}/*左侧div样式*/.left {width: calc(30% - 5px); /*左侧初始…

naive-ui-admin BasicTable 列表操作栏显示图标icon

效果图 在使用BasicTable的页面添加引用&#xff0c;这里随便弄了个icon import { GameController } from "vicons/ionicons5" 自定义列 const actionColumn reactive({width: 180,title: "操作",key: "action",fixed: "right",ren…

【高德地图】Android高德地图控件交互详细介绍

&#x1f4d6;第5章 与地图控件交互 ✅控件交互&#x1f9ca;缩放按钮&#x1f9ca;指南针&#x1f9ca;定位按钮&#x1f9ca;地图Logo ✅手势交互&#x1f9ca;缩放手势&#x1f9ca;滑动手势&#x1f9ca;旋转手势&#x1f9ca;倾斜手势&#x1f9ca;指定屏幕中心点的手势操…

Facebook的虚拟社交愿景:元宇宙时代的新起点

在当今数字化时代&#xff0c;社交媒体已经成为人们生活中不可或缺的一部分。而随着科技的不断进步和社会的发展&#xff0c;元宇宙已经成为了人们关注的热点话题之一。作为社交媒体的领军企业之一&#xff0c;Facebook也在积极探索虚拟社交的未来&#xff0c;将其视为元宇宙时…

社交媒体变革者:剖析Facebook对在线互动的贡献

随着数字化时代的蓬勃发展&#xff0c;社交媒体已经成为人们日常生活中不可或缺的一部分。在这个领域的发展中&#xff0c;Facebook作为先行者和领导者&#xff0c;对在线互动的演变和发展产生了深远的影响。本文将深入剖析Facebook在社交媒体领域的贡献&#xff0c;以及它对在…

贪心算法(算法竞赛、蓝桥杯)--排队接水问题

1、B站视频链接&#xff1a;A25 贪心算法 P1223 排队接水_哔哩哔哩_bilibili 题目链接&#xff1a;排队接水 - 洛谷 #include <bits/stdc.h> using namespace std; struct node{int t,id;//接水时间&#xff0c;编号bool operator<(node &b){return t<b.t;} …

2024-02-23(Spark)

1.RDD的数据是过程数据 RDD之间进行相互迭代计算&#xff08;Transaction的转换&#xff09;&#xff0c;当执行开启后&#xff0c;代表老RDD的消失 RDD的数据是过程数据&#xff0c;只在处理的过程中存在&#xff0c;一旦处理完成&#xff0c;就不见了。 这个特性可以最大化…

Lua速成(2)

一、流程控制 Lua 编程语言流程控制语句通过程序设定一个或多个条件语句来设定。在条件为 true 时执行指定程序代码&#xff0c;在条件为 false 时执行其他指定代码。 控制结构的条件表达式结果可以是任何值&#xff0c;Lua认为false和nil为假&#xff0c;true和非nil为真。 …

NOIP2018-J-4-对称二叉树的题解

原题描述&#xff1a; 题目描述 时间&#xff1a;1s 空间&#xff1a;256M 一棵有点权的有根树如果满足以下条件&#xff0c;则被轩轩称为对称二叉树&#xff1a; 1. 二叉树&#xff1b; 2. 将这棵树所有节点的左右子树交换&#xff0c;新树和原树对应位置的结构相同且…

WinForms中的Timer探究:Form Timer与Thread Timer的差异

WinForms中的Timer探究&#xff1a;Form Timer与Thread Timer的差异 在Windows Forms&#xff08;WinForms&#xff09;应用程序开发中&#xff0c;定时器&#xff08;Timer&#xff09;是一个常用的组件&#xff0c;它允许我们执行定时任务&#xff0c;如界面更新、周期性数据…

QT之项目经验(windows下的sqlite,c++开发)

目录 一、需要时间去磨练gui的调整和优化 1. 借鉴网上开源项目学习 2. gui的布局及调整是磨人的一件事情 3. gui的布局也是可以用组件复刻的 4. 耗时的设备树 二、多线程异步弹窗 三、定时任务动态变更设定 1.确定按钮触发 2.此处监听定时任务时间的改变 3.此处对改变做出具…

Vue 实现页面导出A4标准大小的PDF文件,以及处理图片跨域不能正常展示的问题等

效果预览&#xff1a; 代码流程&#xff1a;首先在utils文件夹下创建htmlToPdf的js工具文件&#xff0c;然后在main.js中注册引用 htmlToPdf.js // 导出页面为PDF格式 import html2Canvas from html2canvas import JsPDF from jspdfexport default {install(Vue, options) {V…

vscode输入英文时字体之间的间隔突然变大,似中文

vscode输入英文时字体之间的间隔突然变大&#xff0c;似中文 主要原因&#xff1a; 是由于输入法变成全角模式了。原因可能是不小心按了 shift空格键快捷键造成的。 正常情况&#xff0c;全角就是字母和数字等与汉字占等宽位置的字。 半角就是ASCII方式的字符&#xff0c;在没…

3、函数定义,函数调用,this指向总结,闭包

一、函数的定义方式 1、函数声明 function demo1() {var num 12var result Math.pow(num,2)//指数函数return result }2、函数表达式 var demo2 function (x,y) { //内置对象arguments前面的两个参数 是 x,yvar sum arguments[0] arguments[1]console.log(sum) }3、构…

stm32用CubeMX库控制OLED显示数字,单个字符,字符串

首先是打开proteus绘制电路图&#xff1a; 接着就是打开CubeMX软件&#xff0c;配置晶振和GPIO口&#xff1a; 接下来就用前面讲过的方法添加一个自己的代码文件夹和代码了&#xff1a; 下面是OLED.c文件&#xff0c;复制就能用&#xff1a; #include "OLED_Font.h"…
最新文章