当前位置: 首页 > news >正文

设计模式20-备忘录模式

设计模式20-备忘录

  • 动机
  • 定义与结构
    • 定义
    • 结构
  • C++代码推导
  • 优缺点
  • 应用场景
  • 总结
  • 备忘录模式和序列化
      • 备忘录模式
        • 1. **动机**
        • 2. **实现方式**
        • 3. **应用场景**
        • 4. **优点**
        • 5. **缺点**
      • 序列化
        • 1. **动机**
        • 2. **实现方式**
        • 3. **应用场景**
        • 4. **优点**
        • 5. **缺点**
      • 对比总结

动机

  • 在软件构建过程中,某些对象的状态在转换过程中可能由于某种需要这个程序能够回溯到对象之前处于某个点时的状态。使用一些公用接口来让其他对象得到对象的状态,便会暴露对象细节的实现
  • 如何实现对象状态的良好保存与恢复?但同时又不会因此而破坏对象本身的封装性呢。
  • 在软件开发中,有时需要在不破坏对象封装性的前提下捕获并存储对象的内部状态,以便稍后可以将对象恢复到以前的状态。这种需求通常出现在实现撤销/恢复操作的场景中,例如文本编辑器中的撤销操作。直接暴露对象的内部状态会违反封装原则,而备忘录模式提供了一种解决方案,使得状态恢复操作能够在不破坏封装性的前提下实现。

定义与结构

定义

备忘录模式在不破坏封装性的前提下,捕获对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将对象恢复到原先保存的状态。

结构

在这里插入图片描述

解释这张图

这张图描绘了一个典型的UML(统一建模语言)结构图,展示了“备忘录模式”(Memento Pattern)的核心组件及其关系。备忘录模式是一种用于捕获和存储一个对象内部状态的方式,以便可以在未来某个时刻将对象恢复到这个状态。这个模式特别适用于需要保存和恢复对象状态的场景,比如撤销操作、事务处理等。

图中包含了三个主要的类:

  1. Originator(原发器):这个类是需要被保存状态的对象。它包含一个CreateMemento()方法,用于创建一个备忘录对象(Memento),这个对象包含了Originator的当前状态。Originator还包含一个SetMemento(Memento m)方法,用于设置Originator的状态为备忘录对象(Memento)中存储的状态。图中对SetMemento方法的描述可能有些误解,因为通常这个方法不会直接从外部传入一个备忘录对象来设置状态,而是由Caretaker或者系统其他部分将之前保存的备忘录对象传递回Originator来恢复状态。

  2. Memento(备忘录):这个类用于存储Originator的内部状态,以保护这些状态不受外部访问。它包含了一个构造函数(尽管图中没有直接标出),该构造函数可能接受来自Originator的当前状态作为参数。Memento还包含GetState()SetState(state)方法(尽管图中SetState方法的参数可能标记有误,通常备忘录模式中的Memento类不需要SetState方法,因为备忘录主要是用来存储状态的)。但在实际实现中,GetState()方法用于返回备忘录中存储的状态,而SetState(如果存在的话)可能并不是Memento类的一部分,而是用于在Originator中恢复状态的过程中的一部分,但这通常是通过将Memento对象传递给Originator的某个恢复状态方法来实现的。

  3. Caretaker(管理者):这个类负责保存备忘录对象,但不检查备忘录对象的内容。在图中,Caretaker类被描述为有一个ReturnNewMemento(state)方法,这实际上可能是一个误解或错误。在标准的备忘录模式中,Caretaker类会维护一个备忘录对象的列表,但不直接创建新的Memento对象。相反,它可能包含一个AddMemento(Memento m)方法来添加新的备忘录对象,以及一个GetMemento(index)或类似的方法来检索之前保存的备忘录对象。图中的ReturnNewMemento(state)可能试图表达Caretaker返回一个新状态的Memento对象给Originator,但这并不是Caretaker类的典型职责;更常见的是,Caretaker简单地存储和检索Memento对象。

图中箭头表示类之间的交互关系。从Originator到Memento的箭头表示Originator创建Memento对象的过程,而从Caretaker到Originator的箭头(尽管图中没有直接画出)表示Caretaker可能将存储的Memento对象传递回Originator以恢复状态。

需要注意的是,图中的某些细节(如ReturnNewMemento(state)方法和SetState0方法的标记)可能与标准的备忘录模式实现不完全一致,可能是为了简化图示或特定实现的表示。

C++代码推导

以下是一个简单的备忘录模式的C++实现示例,模拟一个文本编辑器的撤销功能。

备忘录类:

#include <iostream>
#include <string>// 备忘录类,用于存储Originator的内部状态
class Memento {
private:std::string state;public:Memento(const std::string& state) : state(state) {}std::string getState() const {return state;}
};

发起人类:

class Originator {
private:std::string state;public:void setState(const std::string& state) {this->state = state;std::cout << "State set to: " << state << std::endl;}std::string getState() const {return state;}Memento* createMemento() {return new Memento(state);}void setMemento(Memento* memento) {state = memento->getState();std::cout << "State restored to: " << state << std::endl;}
};

负责人类:

class Caretaker {
private:Memento* memento;public:void saveMemento(Memento* memento) {this->memento = memento;}Memento* getMemento() {return memento;}~Caretaker() {delete memento;}
};

客户端代码:

int main() {Originator* originator = new Originator();Caretaker* caretaker = new Caretaker();originator->setState("State1");caretaker->saveMemento(originator->createMemento());originator->setState("State2");caretaker->saveMemento(originator->createMemento());originator->setState("State3");// 恢复到上一个状态originator->setMemento(caretaker->getMemento());delete originator;delete caretaker;return 0;
}

运行结果:

State set to: State1
State set to: State2
State set to: State3
State restored to: State2

优缺点

优点:

  1. 封装性:备忘录模式可以在不破坏对象封装性的前提下保存和恢复对象的状态,符合对象的封装原则。
  2. 简化撤销操作:备忘录模式简化了复杂系统中的撤销操作实现,使得对象能够恢复到先前的状态。

缺点:

  1. 内存开销:如果对象的状态较大且备忘录创建频繁,可能会占用大量内存,增加系统开销。
  2. 管理复杂性:备忘录需要被妥善管理,尤其是在需要多个备忘录进行多步撤销时,备忘录的管理和恢复可能会变得复杂。

应用场景

备忘录模式在以下场景中应用较多:

  1. 需要保存对象状态以便恢复:例如实现多步撤销/恢复功能的场景,如文本编辑器、图形编辑器等。
  2. 需要避免暴露对象内部状态:当需要避免外部直接访问对象的内部状态时,可以使用备忘录模式来实现状态保存和恢复。
  3. 对象状态变化复杂且不可预测:在对象状态变化频繁且不可预测的情况下,备忘录模式可以有效管理这些状态变化。

总结

  • 备忘录模式是一种强大的设计模式,通过封装对象的内部状态,实现了状态的保存和恢复功能。虽然它能够有效地支持撤销和恢复操作,但在实际应用中需要注意内存消耗和管理复杂性,特别是在对象状态复杂且变化频繁的场景下。
  • 备忘录模式存储原发器对象的内部状态。在需要时恢复原发器状态。
  • 备忘录模式的核心是信息隐藏,即原发器需要向外界隐藏信息保持其封装性,但同时又需要将再保存到外界。
  • 由于现代语言运行时如JAVA ,c#,都具有相当的对象序列化支持。因此往往采用效率较高,又较容易正确实现的序列化方案来实现备忘录模式。

备忘录模式和序列化

备忘录模式和序列化在某些方面具有相似的功能,但它们的目标和应用场景不同。

备忘录模式

1. 动机

备忘录模式的主要动机是保存和恢复对象的内部状态,而不破坏对象的封装性。它常用于实现撤销/恢复操作,让对象能够恢复到之前的某个状态。

2. 实现方式

备忘录模式通过创建一个“备忘录”对象,将发起人(Originator)对象的内部状态存储在该备忘录对象中。发起人可以使用这个备忘录对象来恢复其内部状态。备忘录模式通常包括三个角色:发起人、备忘录和负责人(Caretaker),其中负责人仅负责管理备忘录的保存和恢复,不直接访问备忘录的内容。

3. 应用场景
  • 撤销/恢复操作:如文本编辑器、图形编辑器中的撤销功能。
  • 历史记录管理:需要保存对象的状态以便以后恢复。
  • 避免对象内部状态暴露:需要保存和恢复对象状态时,但不希望暴露对象的内部实现。
4. 优点
  • 封装性:备忘录模式可以在不破坏对象封装性的前提下保存和恢复状态。
  • 简单性:对外界来说,状态的保存和恢复过程是透明的,简化了撤销/恢复功能的实现。
5. 缺点
  • 内存消耗:如果对象的状态较大或备忘录频繁创建,可能会导致较大的内存开销。
  • 管理复杂性:当涉及多个状态备忘录时,备忘录的管理可能会变得复杂。

序列化

1. 动机

序列化的主要目的是将对象的状态转换为字节流,以便可以将其存储在文件、数据库或通过网络传输,并在以后将其反序列化为原始对象。

2. 实现方式

序列化将对象的状态转换为字节流,并将其保存到存储介质中。反序列化则是将字节流重新转换为对象。序列化通常依赖于语言或平台提供的内置机制,如Java的Serializable接口或C++的自定义序列化逻辑。

3. 应用场景
  • 持久化存储:将对象状态保存到文件或数据库中,以便以后恢复。
  • 网络传输:在分布式系统中,将对象状态通过网络传输到另一个系统。
  • 跨平台数据交换:不同平台之间的数据交换,需要将对象状态转换为通用的字节流格式。
4. 优点
  • 持久性:序列化使得对象状态可以长期保存,并在需要时恢复。
  • 跨平台:序列化后的数据可以在不同平台或系统之间传输和共享。
5. 缺点
  • 性能开销:序列化和反序列化可能会引入性能开销,特别是在大型对象或频繁操作的情况下。
  • 安全性:序列化数据可能会暴露对象的内部状态,存在安全风险,如果反序列化的对象来自不可信的来源,可能会导致安全漏洞。

对比总结

  1. 用途不同

    • 备忘录模式:主要用于保存和恢复对象的状态,以支持撤销/恢复操作,强调对象的封装性。
    • 序列化:主要用于持久化或跨网络传输对象的状态,强调对象的持久性和可传输性。
  2. 实现方式不同

    • 备忘录模式:通常是在内存中保存对象的状态,并通过备忘录对象管理状态的保存和恢复。
    • 序列化:将对象的状态转换为字节流,存储在外部介质中或通过网络传输。
  3. 封装性

    • 备忘录模式:注重保持对象的封装性,外部系统无法访问对象的内部状态。
    • 序列化:通常会暴露对象的内部状态,可能会引发安全问题。
  4. 应用场景不同

    • 备忘录模式:适用于需要频繁保存和恢复对象状态的场景,如撤销功能。
    • 序列化:适用于对象的持久化存储、网络传输、跨平台数据交换等场景。

备忘录模式更适合在应用程序内部管理对象的状态转换,而序列化更适合在应用程序与外部系统之间交换或持久化数据。两者可以结合使用,例如在分布式系统中,备忘录模式管理对象状态,而序列化用于将状态持久化或传输到其他节点。

相关文章:

设计模式20-备忘录模式

设计模式20-备忘录 动机定义与结构定义结构 C代码推导优缺点应用场景总结备忘录模式和序列化备忘录模式1. **动机**2. **实现方式**3. **应用场景**4. **优点**5. **缺点** 序列化1. **动机**2. **实现方式**3. **应用场景**4. **优点**5. **缺点** 对比总结 动机 在软件构建过…...

绘制echarts-liquidfill水球图

文章目录 一、效果图二、步骤1.安装插件2.引入2.主要代码2.素材图片 总结 一、效果图 二、步骤 1.安装插件 npm install echarts npm install echarts-liquidfillecharts5的版本与echarts-liquidfill3兼容&#xff0c;echarts4的版本与echarts-liquidfill2兼容,安装的时候需要…...

应急响应:D盾的简单使用.

什么是应急响应. 一个组织为了 应对 各种网络安全 意外事件 的发生 所做的准备 以及在 事件发生后 所采取的措施 。说白了就是别人攻击你了&#xff0c;你怎么把这个攻击还原&#xff0c;看看别人是怎么攻击的&#xff0c;然后你如何去处理&#xff0c;这就是应急响应。 D盾功…...

c语言第14天笔记

通过指针引用数组 数组元素的指针 数组指针&#xff1a;数组中的第一个元素的地址&#xff0c;也就是数组的首地址。 指针数组&#xff1a;用来存放数组元素地址的数组&#xff0c;称之为指针数组。 注意&#xff1a;虽然我们定义了一个指针变量接收了数组地址&#xff0c;但…...

服装行业QMS中的来料检验:常见问题解析与解决策略

在服装行业的来料检验过程中&#xff0c;常会遇到一系列问题&#xff0c;这些问题可能影响到原材料的质量&#xff0c;进而影响最终产品的品质。以下将详细介绍来料检验的常见问题及相应的解决方法&#xff1a; 一、常见问题 外观瑕疵 问题描述&#xff1a;原材料表面存在污渍…...

健身动作AI识别,仰卧起坐计数(含UI界面)

用Python和Mediapipe打造&#xff0c;让你的运动效果一目了然&#xff01; 【技术揭秘】 利用Mediapipe的人体姿态估计&#xff0c;实时捕捉关键点&#xff0c;精确识别动作。 每一帧的关键点坐标和角度都被详细记录&#xff0c;为动作分析提供数据支持。 支持自定义动作训练&a…...

GitHub开源金融系统:Actual

Actual&#xff1a;电子金融&#xff0c;本地优先&#xff0c;自由开源- 精选真开源&#xff0c;释放新价值。 概览 Actual的创新之处在于其对个人财务管理的全面考虑&#xff0c;它不仅仅是一个简单的记账工具&#xff0c;而是一个综合性的理财解决方案。它的本地优先设计意味…...

【学习笔记】Day 7

一、进度概述 1、DL-FWI基础入门培训笔记 2、inversionnet_train 试运行——未成功 二、详情 1、InversionNet: 深度学习实现的反演 InversionNet构建了一个具有编码器-解码器结构的卷积神经网络&#xff0c;以模拟地震数据与地下速度结构的对应关系。 &#xff08;一…...

网络中特殊的 IP 地址

特殊网络 IP 127.0.0.1 127.0.0.1 是本机回送地址&#xff0c;发送到 127.0.0.1 的数据或者从 127.0.0.1 返回的数据只会在本机进行传输, 而不进行外部网络传输。 主要有以下两个作用&#xff1a; 测试本机网络 当我们可以 ping 通 127.0.0.1 的时候, 则说明本机的网卡以及 tc…...

ASP 表单处理入门指南

ASP 表单处理入门指南 简介 ASP(Active Server Pages)是一种由微软开发的服务器端脚本环境,用于动态生成交互性网页。它允许开发者结合HTML、VBScript或JScript脚本语言来创建和运行动态网页或Web应用程序。本文将重点介绍如何使用ASP来处理表单数据,包括表单的创建、数据…...

极米RS10Plus性价比高吗?7款4-6K价位投影仪测评哪款最好

通常家庭想买个投影仪都会选择4-6K这个价位段的投影仪&#xff0c;3K以下的投影配置太低&#xff0c;6K以上的价格略高&#xff0c;4-6K价位段的中高端投影仪正好满足大部分家庭的使用需求。正好极米投影在8月份上新了一款Plus版本的长焦投影&#xff1a;极米RS10Plus&#xff…...

RocketMQ怎么对文件进行读写的?

RocketMQ 对文件的读写主要依赖于其底层的存储机制&#xff0c;核心组件是 CommitLog 和 ConsumeQueue&#xff0c;并且通过 MappedFile 类来进行高效的文件操作。以下是 RocketMQ 文件读写的详细介绍&#xff1a; 1. CommitLog CommitLog 是 RocketMQ 的核心存储文件&#x…...

智慧宠物护理:智能听诊器引领健康监测新潮流

在宠物健康科技的浪潮中&#xff0c;智能听诊器的诞生标志着宠物健康管理迈向了智能化的新纪元。广州坎普利智能信息科技有限公司的创新产品&#xff0c;正为宠物主人和他们的毛茸茸伙伴带来前所未有的关怀体验。 创新特点 这款智能听诊器&#xff0c;以其前沿科技和人性化设…...

SRE工程师第2天:我只要截图功能 而不是打开微信

大家好&#xff0c;我是watchpoints 别想太多&#xff0c;只管去提问,所有问题&#xff0c;都会有答案 watchpoints是我github用户名 &#xff0c; 也是我的wechat 用户名&#xff0c;如果我有讲不明白 欢迎提问 什么是SRE&#xff08;Site Reliability Engineer&#xff09; 和…...

【RunnerGo】离线安装成功版本

目录 一、下载 二、解压安装包 三、修改安装配置 3.1 编辑修改安装参数&#xff08;我没有改&#xff0c;默认安装即可&#xff09; 3.2 安装目录结构说明 四、执行安装 五、检查服务并使用 六、访问 前言&#xff1a;最近在调研一个新工具&#xff0c;发现RunnerGo&…...

AI 手机的技术展望

某某领导问到我&#xff0c;AI手机这个产业发展如何&#xff1f;对于&#xff0c;地方科技园区&#xff0c;应该如何发展相关产业&#xff1f;我一时还真说不上来&#xff0c;于是&#xff0c;查了一下资料&#xff0c;大概应对了一下。 一&#xff1a;AI手机的定义 首先&…...

实战 Springboot2 集成Redis 哨兵模式、集群模式、缓存管理、Lettuce拓扑刷新

redis搭建集群模式、Cluster模式&#xff08;6节点&#xff0c;3主3从集群模式&#xff0c;添加删除节点&#xff09;_redis cluster节点带数据增减-CSDN博客 Linux部署Redis哨兵集群 一主两从三哨兵&#xff08;这里使用Redis6&#xff0c;其它版本类似&#xff09;_linux red…...

MYSQL--binlog和redo log

前言 MySQL日志 MySQL日志主要包括错误日志、查询日志、慢查询日志、事务日志、二进制日志几大类。其中比较重要的就是二进制日志binlog&#xff08;归档日志&#xff09;、事务日志redo log&#xff08;重做日志&#xff09;和undo log&#xff08;回滚日志&#xff09;。 这篇…...

R语言医疗数据分析笔记

分组因子又是什么意思&#xff0c;分组因子和数组的区别是什么 举个实际的例子 分组因子 分组因子是分类变量&#xff0c;用于将数据分成不同组以便于比较或分析。例如&#xff0c;在一项研究中&#xff0c;研究对象的性别&#xff08;男性和女性&#xff09;可以视为一个分组…...

SpringBoot使用Jackson-XML裁剪多余的根节点

相关博客&#xff1a;《SpringBoot集成WebService(wsdl)》 比如我们有以下入参 我们只需要MedicalCardInfo这个根节点&#xff0c;其余都不要。如何处理&#xff1f; <A><B><Sender>Aa</Sender><MsgType>Bb</MsgType><MsgVersion>…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源&#xff08;HTML/CSS/图片等&#xff09;&#xff0c;响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址&#xff0c;提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

JVM垃圾回收机制全解析

Java虚拟机&#xff08;JVM&#xff09;中的垃圾收集器&#xff08;Garbage Collector&#xff0c;简称GC&#xff09;是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象&#xff0c;从而释放内存空间&#xff0c;避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具

文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f608;sinx波动的基本原理 三、&#x1f608;波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、&#x1f30a;波动优化…...

Docker 本地安装 mysql 数据库

Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker &#xff1b;并安装。 基础操作不再赘述。 打开 macOS 终端&#xff0c;开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

Kafka入门-生产者

生产者 生产者发送流程&#xff1a; 延迟时间为0ms时&#xff0c;也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于&#xff1a;异步发送不需要等待结果&#xff0c;同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...

【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制

目录 节点的功能承载层&#xff08;GATT/Adv&#xff09;局限性&#xff1a; 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能&#xff0c;如 Configuration …...