设计模式学习笔记 - 设计模式与范式 -行为型:14.备忘录模式:对于大对象的备份和恢复,如何优化内存和时间的消耗

概述

上两篇文章,我们学习了访问者模式的原理与实现、以及为什么支持双分派编程语言不需要访问者模式。

本章,学习另外一种行为型模式,备忘录模式。这个模式理解、掌握起来不难,代码实现比较灵活,应用场景也比较明确和有效,主要是用来防丢失、撤销、恢复等。


备忘录模式的原理与实现

备忘录模式,也叫快照模式,应为翻译为 Memento Design Pattern。在 GoF 的《设计模式》中,备忘录模式是这么定义的:

Captures and externalizes an object’s internal state so that it can be restored later,all without violating encapsulation.

翻译成中文:在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保持这个状态,以便之后恢复对象为先前的状态。

这个模式的定义表达了两部分的内容。

  • 一部分是,存储副本以便后期恢复。
  • 另一部分是,要在不违背封装原则的前提下,进行对象的备份和恢复。

第二部分不太好理解,下面结合一个例子来解释下,你需要搞清楚两个问题:

  • 为什么存储副本和恢复副本会违背封装原则。
  • 备忘录模式是如何做到不违背封装原则的?

假设有这样一个面试题,希望你编写一个小程序,可以接收命令输入。用户输入文本时,程序将追加存储在内存文本中;用户输入 “:list”,程序在命令行中输出文本的内容;用户输入 “:undo”,程序会撤销上一次输入的文本,也就是从内存文本中将上一次输入的文本删除。

>hello
>:list
hello
>world
>:list
helloword
>:undo
>:list
hello

怎么编程实现呢?从整体上来看,这个程序实现起来并不复杂。下面是我写的一种实现思路。

public class InputText {private StringBuilder text = new StringBuilder();public String getText() {return text.toString();}public void append(String input) {text.append(input);}public void setText(String text) {this.text.replace(0, this.text.length(), text);}
}public class SnapshotHolder {private Stack<InputText> snapshots = new Stack<>();public InputText popSnapshot() {return snapshots.pop();}public void pushSnapshot(InputText inputText) {snapshots.push(inputText);}
}public class Application {public static void main(String[] args) {InputText inputText = new InputText();SnapshotHolder snapshotHolder = new SnapshotHolder();Scanner scanner = new Scanner(System.in);while (scanner.hasNext()) {String input = scanner.next();if (input.equals(":list")) {System.out.println(inputText.getText());} else if (input.equals(":undo")) {InputText snapshot = snapshotHolder.popSnapshot();inputText.setText(snapshot.getText());} else {snapshotHolder.pushSnapshot(inputText);inputText.append(input);}}}
}

实际上备忘录模式很灵活,也没有固定的实现方式,在不同的业务需求、不同编程语言下,代码实现可能不一样。上面的代码基本上已经实现了最基本的备忘录的功能。但是,如果深究一下的话,还有一些问题要解决,那就是定义中的第二部分:要在不违背封装原则的前提下,进行对象的备份和恢复。而上面的代码并不满足这一点,主要体现在两个方面:

  • 第一,为了能快照恢复 InputText 对象,我们在 InputText 类中定义了 setText() 函数,这个函数有可能会被其他业务使用,所以,暴露不应该暴露的函数违背了封装原则。
  • 第二,快照本身是不可变的,理论上将,不应该包含 set() 等修改内部状态的函数,但在上面的代码实现中,“快照” 这个业务模式复用了 InputText 类的定义,而 InputText 类本身有一些列修改内部状态的函数,所以,用 InputText 来表示快照违背了封装原则。

针对以上问题,对代码做两点修改。

  • 其一,定义一个独立的类(Snapshot)来表示快照,而不是复用 InputText 类。这个类只暴露 get() 方法。
  • 其二,在 InputText 中,我们把 setText() 方法重命名为 restoreSnapshot() 方法,用意更加明确,只用来恢复对象。

按照这个思路,对代码进行重构。

public class InputText {private StringBuilder text = new StringBuilder();public String getText() {return text.toString();}public void append(String input) {text.append(input);}public Snapshot createSnapshot() {return new Snapshot(text.toString());}public void restoreSnapshot(Snapshot snapshot) {this.text.replace(0, this.text.length(), snapshot.getText());}
}public class Snapshot {private String text;public Snapshot(String text) {this.text = text;}public String getText() {return text;}
}public class SnapshotHolder {private Stack<Snapshot> snapshots = new Stack<>();public Snapshot popSnapshot() {return snapshots.pop();}public void pushSnapshot(Snapshot snapshot) {snapshots.push(snapshot);}
}public class Application {public static void main(String[] args) {InputText inputText = new InputText();SnapshotHolder snapshotHolder = new SnapshotHolder();Scanner scanner = new Scanner(System.in);while (scanner.hasNext()) {String input = scanner.next();if (input.equals(":list")) {System.out.println(inputText.getText());} else if (input.equals(":undo")) {Snapshot snapshot = snapshotHolder.popSnapshot();inputText.restoreSnapshot(snapshot);} else {snapshotHolder.pushSnapshot(inputText.createSnapshot());inputText.append(input);}}}
}

实际上,上面的代码就是典型的备忘录模式的代码实现。

除了备忘录模式,还有一个和它很类似的概念,“备份”,它在我们平时的开发中经常听到。那备忘录模式和 “备份” 有什么区别呢?实际上,这两者的应用场景很类似,都应用在防丢失、恢复、撤销等场景中。它们的区别在于,备忘录模式更侧重于代码设计和实现,备份更侧重架构设计或产品设计。

如何优化内存和时间消耗?

前面只是简单介绍了备忘录模式的原理和经典实现,现在再继续深挖下。如果要备份的对象数据比较大,备份的频率又比较高,那快照占用的内存会比较大,备份和恢复的耗时会比较长。这个问题该如何解决呢?

不同的应用场景有不同的解决办法。比如,前面的例子,应用场景就是利用备忘录来实现撤销操作,而且仅仅支持顺序撤销,也就是说,每次操作只能撤销上一次的输入,不能跳过上次输入撤销之前的输入。在具有这样特点的应用场景下,为了节省内存,我们不需要在快照中存储完整的文本,只需要记录少许信息,比如在获取快照当下的文本长度,用这个值结合 InputText 类对象存储的文本来做撤销操作。

再举一个例子。假设每当有数据改动,都需要生成一个备份,以备之后恢复。如果需要备份的数据很大,这样高频率的备份,不管是对存储的消耗,还是对时间的消耗,都可能是无法接受的。想要解决这个问题,一般会采用 “低频率全量备份” 和 “高频率增量备份” 相结合的方法。

全量备份和上面的例子类似,就是把所有的数据 “拍个快照” 保存下来。所谓 “增量备份”,指的是记录每次操作或数据变动。

当我们需要恢复到某一时间点的备份时,如果这一时间点有做全量备份,我们直接拿来恢复即可。如果这一时间点没有对应的全量备份,就先找到最近一次的全量备份,然后用它来恢复,之后执行此次全量备份跟这一时间点之间的所有增量备份,也就是对应的操作或者数据变动。这样就能减少全量备份的数据和频率,减少对时间、内存的消耗。

总结

备忘录模式也叫快照模式。具体来说,就是在不违背封装原则的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便之后恢复对象为先前的状态。这个模式的定义表达了两部分内容:一部分是,存储副本以便后期恢复;另一部分是,要在不违背封装原则的前提下,进行对象的备份和恢复。

备忘录模式的应用场景也比较明确和有效,主要是用来防丢失、撤销、恢复等。它跟平时我们说的 “备份” 很相似。两者的主要区别在于,备忘录模式更侧重于代码的设计和实现,“备份” 更侧重架构设计或产品设计。

对于大对象的备份来说,备份占用的存储空间会比较大,备份和恢复的耗时会比较长。针对这个问题,不同的业务场景有不同的处理方式。

  • 比如,只备份必要的恢复信息,结合最新的数据来恢复。
  • 再比如,全量备份和增量备份相结合,低频全量备份,高频增量备份,两种结合来恢复。

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

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

相关文章

C++设计模式|创建型 1.单例模式

1.什么是单例模式&#xff1f; 单例模式的的核⼼思想在创建类对象的时候保证⼀个类只有⼀个实例&#xff0c;并提供⼀个全局访问点来访问这个实例。 只有⼀个实例的意思是&#xff0c;在整个应⽤程序中&#xff0c;只存在该类的⼀个实例对象&#xff0c;⽽不是创建多个相同类…

【JAVA基础篇教学】第八篇:Java中List详解说明

博主打算从0-1讲解下java基础教学&#xff0c;今天教学第八篇&#xff1a;Java中List详解说明。 在 Java 编程中&#xff0c;List 接口是一个非常常用的集合接口&#xff0c;它代表了一个有序的集合&#xff0c;可以包含重复的元素。List 接口提供了一系列操作方法&#xff0c;…

72V电瓶电压降5V1.5A恒压WT7039

72V电瓶电压降5V1.5A恒压WT7039 WT6039是一款12V至72V宽电压降压DC-DC转换器芯片&#xff0c;集成了开关控制、参考电源、误差放大器等多重功能&#xff0c;并具备过热、限流和短路保护。它能在广泛输入功率下实现2A连续输出电流&#xff0c;并通过编程调整输出电压。适用于高…

文心一言

文章目录 前言一、首页二、使用总结 前言 今天给大家带来百度的文心一言,它基于百度的文心大模型,是一种全新的生成式人工智能工具。 一、首页 首先要登录才能使用,左侧可以看到以前的聊天历史 3.5的目前免费用,但是4.0的就需要vip了 二、使用 首先在最下方文本框输入你想要搜…

ArrayList,Vector,LinkedList内存解析

1.ArrayList (1). 特点 : 实现了List接口&#xff0c;存储有序的&#xff0c;可重复的数据.底层使用Object[]数组存储.线程不安全.(底层方法未用synchronized修饰.) (2). 版本解析 : <i> : JDK7版本 ArrayList<String> list &#xff1d;new ArrayList<&…

npm问题合集以及npm常规操作整理

问题一&#xff1a;npm install 卡在“sill idealTree buildDeps“ 可以尝试将网络切换为其他网络&#xff0c;我的是这么解决的。可以尝试重新设置镜像。 问题二&#xff1a;npm install 项目依赖报WARN deprecated 原因 依赖版本问题&#xff0c;下载最新版本。 解决方案 …

做多 与 做空 概念解析

坐多&#xff1a;低买高卖 阶段一&#xff1a;借钱->低价时买货>得货 阶段二&#xff1a;高价时卖货 > 得更多的钱 阶段三&#xff1a;还钱 >余钱 多空&#xff1a;高卖低买 阶段一&#xff1a;借货->高价时卖货>得钱 阶段二&#xff1a;低价时买货 …

ElasticSearch中使用bge-large-zh-v1.5进行向量检索(一)

一、准备 系统&#xff1a;MacOS 14.3.1 ElasticSearch&#xff1a;8.13.2 Kibana&#xff1a;8.13.2 BGE是一个常见的文本转向量的模型&#xff0c;在很多大模型RAG应用中常常能见到&#xff0c;但是ElasticSearch中默认没有。BGE模型有很多版本&#xff0c;本次采用的是bg…

Lua语法(四)——协程

参考链接: 系列链接: Lua语法(一) 系列链接: Lua语法(二)——闭包/日期和时间 系列链接: Lua语法(三)——元表与元方法 系列链接: Lua语法(四)——协程 系列链接: Lua语法(五)——垃圾回收 系列链接: Lua语法(六)——面相对象编程 Lua语法 四——协程 简介正文协程coroutine.c…

vue和react通用后台管理系统权限控制方案

1. 介绍 在任何企业级应用中&#xff0c;尤其是后台管理系统&#xff0c;权限控制是一个至关重要的环节。它确保了系统资源的安全性&#xff0c;防止非法访问和操作&#xff0c;保障业务流程的正常进行。本文件将详细解析后台管理系统中的权限控制机制及其实施策略。 那么权限…

写代码的修养

看山是山&#xff0c;看水是水 此境界 对业务的思考是浅层的&#xff0c;代码写的不通用&#xff0c;扩展性差&#xff0c;表现在无设计模式 看山不是山&#xff0c;看水不是水 此境界 对业务的思考是中层的&#xff0c;代码写的通用&#xff0c;扩展性好&#xff0c;表现为…

算法思想总结:分治思想

一、颜色划分 . - 力扣&#xff08;LeetCode&#xff09; class Solution { public:void sortColors(vector<int>& nums) {//三路划分的思想int nnums.size();int left-1, rightn,cur0;while(cur<right){if(nums[cur]0) swap(nums[left],nums[cur]);else if(nums…

关于openai和chatgpt、gpt-4、PyTorch、TensorFlow 两者和Transformers的关系

近两年&#xff0c;随着人工智能的火爆&#xff0c;不论通过哪个渠道&#xff0c;相信我们都听说过openai、gpt等这类名词&#xff0c;那么它们到底是什么意思&#xff0c;请看下文。 openai:是一家人工智能公司&#xff1b; openai-api&#xff1a;是openai提供的api&#xf…

ChatGPT加持,需求分析再无难题

简介 在实际工作过程中&#xff0c;常常需要拿到产品的PRD文档或者原型图进行需求分析&#xff0c;为产品的功能设计和优化提供建议。 而使用ChatGPT可以很好的帮助分析和整理用户需求。 实践演练 接下来&#xff0c;需要使用ChatGPT 辅助我们完成需求分析的任务 注意&…

GMSSL-通信

死磕GMSSL通信-C/C++系列(一) 最近再做国密通信的项目开发,以为国密也就简单的集成一个库就可以完事了,没想到能有这么多坑。遂写下文章,避免重复踩坑。以下国密通信的坑有以下场景 1、使用GMSSL guanzhi/GmSSL进行通信 2、使用加密套件SM2-WITH-SMS4-SM3 使用心得 ​…

电动汽车原理视频笔记

看到了一个讲的不错的系列视频 新能源维修猿老罗的个人空间-新能源维修猿老罗个人主页-哔哩哔哩视频 新能源汽车上的安全防护系统就是这么多&#xff01;-绝缘检测等_哔哩哔哩_bilibili 新能源汽车居然是这样上电的-高低压上下电详细流程_哔哩哔哩_bilibili

机器学习和深度学习-- 李宏毅(笔记与个人理解)Day 14

Day 14 Classfication (short version) 二分类的时候 用sigmoid 那不就是 logistic 回归嘛&#xff08;softmax 的二分类等价&#xff09; Loss 哦 今天刚学的 &#xff0c;KL散度 &#xff0c;看来cross-entropy 和KL散度是等价的咯~ 我感觉我的直觉没错 这里MSE离得很远的时候…

php未能在vscode识别?

在设置里搜php&#xff0c;找到settings.json&#xff0c;设置你的安装路径即可。 成功

HubSpot如何通过自动化和优化客户服务流程?

在当今竞争激烈的市场环境中&#xff0c;提供卓越的客户服务体验已经成为企业赢得客户忠诚、推动业务增长的关键所在。HubSpot&#xff0c;作为一款领先的客户关系管理软件&#xff0c;通过自动化和优化客户服务流程&#xff0c;为企业带来了革命性的服务体验提升。 HubSpot通…

【今日bug】在本地创建容器,进入后快速退出

容器已启动但立即退出&#xff1a; 根据 STATUS 列显示的 Exited (1) 5 seconds ago&#xff0c;容器确实已被启动&#xff0c;但几乎立刻就退出了。退出状态码为 1&#xff0c;通常表示在容器内部执行的命令&#xff08;这里是 /bin/bash&#xff09;遇到非零退出状态&#xf…