设计模式 21 备忘录模式 Memento Pattern

设计模式 21 备忘录模式 Memento Pattern
1.定义

        备忘录模式是一种行为型设计模式,它允许你将一个对象的状态保存到一个独立的“备忘录”对象中,并在之后恢复到该状态。


2.内涵

主要用于以下场景:

需要保存对象状态以备恢复: 当你想要在进行一些可能导致对象状态改变的操作之前,先保存对象当前状态,以便在操作失败或需要撤销操作时能够恢复到之前的状态。
需要提供撤销/重做功能: 备忘录模式可以用来实现撤销和重做功能,通过保存一系列状态,用户可以回退到之前的操作步骤。
需要将对象状态序列化: 备忘录模式可以将对象状态序列化到一个独立的对象中,方便存储或传输。

主要模块如下:

  • 发起者(Originator): 需要保存状态的对象。
  • 备忘录(Memento): 保存发起者状态的对象。
  • 守护者(Caretaker): 负责管理备忘录对象,通常会保存多个备忘录,以便实现撤销/重做功能。

解释:

Originator 对象拥有一个 createMemento() 方法,用于创建 Memento 对象并保存自身状态。同时,它也拥有一个 setMemento() 方法,用于从 Memento 对象中恢复自身状态。
Memento 对象保存了 Originator 对象的状态,并且只允许 Originator 对象访问其内部状态。
Caretaker 对象负责管理 Memento 对象,它拥有 addMemento() 和 getMemento() 方法,分别用于添加和获取 Memento 对象。
类图关系:

Originator 对象与 Memento 对象之间存在关联关系,因为 Originator 对象创建并使用 Memento 对象。
Caretaker 对象与 Memento 对象之间也存在关联关系,因为 Caretaker 对象管理 Memento 对象。


3.案例分析

class Memento {public:virtual ~Memento() {}virtual std::string GetName() const = 0;virtual std::string date() const = 0;virtual std::string state() const = 0;
};/*** The Concrete Memento contains the infrastructure for storing the Originator's* state.*/
class ConcreteMemento : public Memento {private:std::string state_;std::string date_;public:ConcreteMemento(std::string state) : state_(state) {this->state_ = state;std::time_t now = std::time(0);this->date_ = std::ctime(&now);}/*** The Originator uses this method when restoring its state.*/std::string state() const override {return this->state_;}/*** The rest of the methods are used by the Caretaker to display metadata.*/std::string GetName() const override {return this->date_ + " / (" + this->state_.substr(0, 9) + "...)";}std::string date() const override {return this->date_;}
};/*** The Originator holds some important state that may change over time. It also* defines a method for saving the state inside a memento and another method for* restoring the state from it.*/
class Originator {/*** @var string For the sake of simplicity, the originator's state is stored* inside a single variable.*/private:std::string state_;std::string GenerateRandomString(int length = 10) {const char alphanum[] ="0123456789""ABCDEFGHIJKLMNOPQRSTUVWXYZ""abcdefghijklmnopqrstuvwxyz";int stringLength = sizeof(alphanum) - 1;std::string random_string;for (int i = 0; i < length; i++) {random_string += alphanum[std::rand() % stringLength];}return random_string;}public:Originator(std::string state) : state_(state) {std::cout << "Originator: My initial state is: " << this->state_ << "\n";}/*** The Originator's business logic may affect its internal state. Therefore,* the client should backup the state before launching methods of the business* logic via the save() method.*/void DoSomething() {std::cout << "Originator: I'm doing something important.\n";this->state_ = this->GenerateRandomString(30);std::cout << "Originator: and my state has changed to: " << this->state_ << "\n";}/*** Saves the current state inside a memento.*/Memento *Save() {return new ConcreteMemento(this->state_);}/*** Restores the Originator's state from a memento object.*/void Restore(Memento *memento) {this->state_ = memento->state();std::cout << "Originator: My state has changed to: " << this->state_ << "\n";}
};/*** The Caretaker doesn't depend on the Concrete Memento class. Therefore, it* doesn't have access to the originator's state, stored inside the memento. It* works with all mementos via the base Memento interface.*/
class Caretaker {/*** @var Memento[]*/private:std::vector<Memento *> mementos_;/*** @var Originator*/Originator *originator_;public:Caretaker(Originator* originator) : originator_(originator) {}~Caretaker() {for (auto m : mementos_) delete m;}void Backup() {std::cout << "\nCaretaker: Saving Originator's state...\n";this->mementos_.push_back(this->originator_->Save());}void Undo() {if (!this->mementos_.size()) {return;}Memento *memento = this->mementos_.back();this->mementos_.pop_back();std::cout << "Caretaker: Restoring state to: " << memento->GetName() << "\n";try {this->originator_->Restore(memento);} catch (...) {this->Undo();}}void ShowHistory() const {std::cout << "Caretaker: Here's the list of mementos:\n";for (Memento *memento : this->mementos_) {std::cout << memento->GetName() << "\n";}}
};/*** Client code.*/void ClientCode() {Originator *originator = new Originator("Super-duper-super-puper-super.");Caretaker *caretaker = new Caretaker(originator);caretaker->Backup();originator->DoSomething();caretaker->Backup();originator->DoSomething();caretaker->Backup();originator->DoSomething();std::cout << "\n";caretaker->ShowHistory();std::cout << "\nClient: Now, let's rollback!\n\n";caretaker->Undo();std::cout << "\nClient: Once more!\n\n";caretaker->Undo();delete originator;delete caretaker;
}int main() {std::srand(static_cast<unsigned int>(std::time(NULL)));ClientCode();return 0;
}

4.注意事项

备忘录模式在使用时需要注意以下几点:

1. 备忘录的封装性:

备忘录对象应该封装 Originator 的内部状态,并对外部隐藏这些状态。这意味着 Caretaker 只能通过 Originator 提供的接口来访问备忘录对象,而不能直接访问备忘录对象内部的状态。
这可以防止 Caretaker 意外修改 Originator 的状态,并确保状态的一致性。


2. 备忘录的管理:

Caretaker 应该负责管理备忘录对象,例如存储、检索和删除备忘录。
Caretaker 应该提供方法来添加、获取和删除备忘录,以便 Originator 可以方便地保存和恢复其状态。


3. 备忘录的性能:

创建和存储备忘录对象可能会消耗一定的资源,因此需要考虑性能问题。
可以通过使用轻量级的备忘录对象或使用缓存机制来提高性能。


4. 备忘录的安全性:

备忘录对象可能包含敏感信息,因此需要考虑安全性问题。
可以使用加密或访问控制机制来保护备忘录对象。


5. 备忘录的使用场景:

备忘录模式适用于需要保存和恢复对象状态的场景,例如撤销/重做功能、游戏存档和恢复等。
需要根据具体场景选择合适的备忘录实现方式,例如使用简单的数据结构或使用更复杂的序列化机制。

5.最佳实践

在使用备忘录模式时,以下是一些值得遵循的经验:

1. 保持备忘录的简单性:

备忘录对象应该只包含 Originator 状态的必要信息,避免过度复杂化。
尽量使用简单的数据结构来存储状态,例如字典、列表等,避免使用复杂的自定义对象。
2. 使用工厂模式创建备忘录:

使用工厂模式可以简化备忘录对象的创建过程,并隐藏备忘录对象的具体实现细节。
工厂模式可以根据不同的场景创建不同的备忘录对象,例如创建不同的备忘录类型来保存不同的状态信息。
3. 使用策略模式管理备忘录:

使用策略模式可以根据不同的需求选择不同的备忘录管理策略,例如使用不同的存储方式或不同的访问控制机制。
策略模式可以提高代码的可扩展性和灵活性,方便根据需求进行修改和扩展。
4. 使用缓存机制提高性能:

可以使用缓存机制来存储最近使用的备忘录对象,避免重复创建备忘录对象。
缓存机制可以提高性能,尤其是在频繁保存和恢复状态的场景下。
5. 使用序列化机制保存备忘录:

可以使用序列化机制将备忘录对象保存到文件或数据库中,以便持久化保存状态。
序列化机制可以方便地保存和恢复状态,并支持跨平台使用。
6. 考虑安全性问题:

如果备忘录对象包含敏感信息,需要考虑安全性问题,例如使用加密或访问控制机制来保护备忘录对象。
可以使用安全策略来限制对备忘录对象的访问权限,并确保信息的安全性。

6.总结

备忘录模式是一种强大的设计模式,可以帮助我们实现状态的保存和恢复功能。在使用备忘录模式时,需要注意封装性、管理、性能、安全性以及使用场景等方面,以确保其有效性和安全性。

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

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

相关文章

torch.matmul()的用法

这篇文章记录torch.matmul()的用法 这里仿照官方文档中的例子说明&#xff0c;此处取整数随机数&#xff0c;用于直观的查看效果&#xff1a; vector x vector 两个一维向量的matmul相当于点积&#xff0c;得到一个标量 tensor1 torch.randint(1, 6, (3,)) tensor2 torch.…

机器学习基础笔记

周志华老师的机器学习初步的笔记 绪论 知识分类 科学 是什么&#xff0c;为什么 技术 怎么做 工程 多快好省 应用 口诀&#xff0c;技巧&#xff0c;实际复杂环境&#xff0c;行行出状元 定义 经典定义 利用经验改善系统自身的性能 训练数据 模型 学习算法 分类 决策树…

Django5+React18前后端分离开发实战14 React-Router6 入门教程

使用nodejs18 首先&#xff0c;将nodejs切换到18版本&#xff1a; nvm use 18创建项目 npm create vitelatest zdpreact_basic_router_dev -- --template react cd zdpreact_basic_router_dev npm install react-router-dom localforage match-sorter sort-by npm run dev此…

kafka跨地区跨集群同步工具MirrorMaker2 —— 筑梦之路

MM2简介 KIP-382: MirrorMaker 2.0 - Apache Kafka - Apache Software Foundation 有四种运行MM2的方法&#xff1a; As a dedicated MirrorMaker cluster.&#xff08;作为专用的MirrorMaker群集&#xff09; As a Connector in a distributed Connect cluster.&#xff08…

使用IDEA远程debug调试

文章目录 应用背景开启方式IDEA设置启动脚本改造 参考资料 应用背景 springboot项目&#xff0c;部署到服务器上&#xff0c;需要开启远程debug跟踪代码。 使用idea开启远程debug。 开启方式 IDEA设置 选择 Edit Configuration 如图&#xff0c;点击加号&#xff0c;选择Re…

【机器学习】利用机器学习优化陆军战术决策与战场态势感知

&#x1f512;文章目录&#xff1a; &#x1f4a5;1.引言 &#x1f6f4;2.机器学习在陆军战术决策中的应用 &#x1f6e3;️2.1数据收集与预处理 &#x1f304;2.2模型构建与训练&#xff1a; &#x1f305;2.3实时决策支持&#xff1a; &#x1f305;2.4代码实现 &…

排序算法——上

一、冒泡排序&#xff1a; 1、冒泡排序算法的思想 我们从左边开始把相邻的两个数两两做比较&#xff0c;当一个元素大于右侧与它相邻的元素时&#xff0c;交换它们之间位置&#xff1b;反之&#xff0c;它们之间的位置不发生变化。冒泡排序是一种稳定的排序算法。 2、代码实现…

5月20日分割等和子集+最后一块石头的重量Ⅱ

416.分割等和子集 给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集&#xff0c;使得两个子集的元素和相等。 示例 1&#xff1a; 输入&#xff1a;nums [1,5,11,5] 输出&#xff1a;true 解释&#xff1a;数组可以分割成 [1, 5, 5] 和…

【ai】LiveKit Agent 的example及python本地开发模式工程实例

title: ‘LiveKit Agent Playground’ playgroundLiveKit Community playground的环境变量&#xff1a;LiveKit API # LiveKit API Configuration LIVEKIT_API_KEYYOUR_API_KEY LIVEKIT_API_SECRETYOUR_API_SECRET# Public configuration NEXT_PUBLIC_LIVEKIT_URLwss://YOUR_…

JAVA智慧工厂制造生产管理MES系统,全套源码,多端展示(MES与ERP系统的区别和联系)

MES与ERP系统的区别和联系 MES制造执行系统&#xff0c;是一套面向制造公司车间执行层的生产信息化管理系统。MES 可觉得公司提供涉及制造数据管理、计划排产管理、生产调度管理、库存管理、质量管理、人力资源管理、工作中心、设备管理、工具工装管理、采购管理、成本管理、项…

为什么推荐前端用WebStorm软件编程?

一、介绍 WebStorm是由JetBrains公司开发的一款JavaScript开发工具&#xff0c;被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。它支持JavaScript、ECMAScript 6、TypeScript、CoffeeScript、Dart和Flow等多种语言的代码…

大学搜题软件音乐类?分享三个支持答案和解析的工具 #微信#媒体

高效的学习工具可以帮助我们提高记忆力和理解能力&#xff0c;使知识更加深入人心。 1.彩虹搜题 这是个微信公众号 一款专门供全国大学生使用的查题神器!致力于帮助大学生解决学习上的难题,涵盖了大学生学习所需的学习资料。 下方附上一些测试的试题及答案 1、甲、乙合伙开…

goimghdr,一个有趣的 Python 库!

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个有趣的 Python 库 - goimghdr。 Github地址&#xff1a;https://github.com/corona10/goimghdr 在图像处理和分析过程中&#xff0c;识别图像文件的类型是一个常见的需求。Python自带的imghdr…

开源与闭源:AI模型发展的两条路径

目录 前言1 数据隐私保护与用户数据安全1.1 开源大模型的透明性与挑战1.2 闭源大模型的控制与责任 2 商业应用的优劣比较2.1 开源大模型的灵活性与创新2.2 闭源大模型的可靠性与服务质量 3 社区参与与合作的差异3.1 开源大模型的社区驱动与协作3.2 闭源大模型的企业主导与保密性…

【openlayers系统学习】3.3假彩色图像合成(三个波段合成假彩色图像)

三、假彩色图像合成 在上一步中&#xff0c;我们使用 ol/source/GeoTIFF​ 源从单个多波段源&#xff08;具有红色、绿色、蓝色和Alpha波段&#xff09;渲染真彩色图像。在下面这个例子中&#xff0c;我们将从可见光谱之外提取数据&#xff0c;并使用它来呈现假彩色合成。 我…

【基于 PyTorch 的 Python 深度学习】9 目标检测与语义分割(2)

前言 文章性质&#xff1a;学习笔记 &#x1f4d6; 学习资料&#xff1a;吴茂贵《 Python 深度学习基于 PyTorch ( 第 2 版 ) 》【ISBN】978-7-111-71880-2 主要内容&#xff1a;根据学习资料撰写的学习笔记&#xff0c;该篇主要介绍了优化候选框的几种方法。 一、优化候选框的…

抖店怎么选品?抖店爆款选品思路技巧,新手直接用!

大家好&#xff0c;我是电商花花。 抖店选品永远是我们做抖店&#xff0c;做电商的核心&#xff0c;店铺想要出单&#xff0c;想要赚钱&#xff0c;我们就一定要学会怎么选品&#xff0c;怎么筛选商品。 而我们绝大多数新手并没有办法保证持续选爆款的能力&#xff0c;如果店…

【ARM+Codesys案例】T3/RK3568/树莓派+Codesys锂电池测试设备控制解决方案

锂电池诞生于上世纪60年代&#xff0c;90年代开始由日本索尼公司实现商业化。锂离子电池凭借快速充放电、长循环寿命、无记忆效应等众多优点&#xff0c;成为当今数码产品及电动汽车大规模应用的第一选择。与镍氢电池、铅酸电池相比&#xff0c;锂电池可以存储更多电能。现在&a…

Visual Studio 智能代码插件:CodeGeeX

前言 在软件开发领域&#xff0c;高效的编程助手一直是提升开发者效率和质量的关键。 随着人工智能技术的不断发展&#xff0c;智能编程助手逐渐成为开发者们不可或缺的工具。其中&#xff0c;CodeGeeX作为一款专为Visual Studio设计的免费智能编程助手&#xff0c;凭借其强大…

让大模型更聪明——复杂而艰巨的任务

一、引言 在人工智能领域&#xff0c;大模型因其强大的数据处理能力和复杂的结构&#xff0c;成为了推动技术进步的重要力量。然而&#xff0c;要让大模型真正展现出“聪明”的特质&#xff0c;即具备高度的人类智能水平&#xff0c;仍是一项极具挑战性的任务。本文将从数据质…