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

2024-05-28 19:12

本文主要是介绍设计模式 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.总结

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

这篇关于设计模式 21 备忘录模式 Memento Pattern的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1011444

相关文章

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

SpringBoot如何通过Map实现策略模式

《SpringBoot如何通过Map实现策略模式》策略模式是一种行为设计模式,它允许在运行时选择算法的行为,在Spring框架中,我们可以利用@Resource注解和Map集合来优雅地实现策略模式,这... 目录前言底层机制解析Spring的集合类型自动装配@Resource注解的行为实现原理使用直接使用M

C#原型模式之如何通过克隆对象来优化创建过程

《C#原型模式之如何通过克隆对象来优化创建过程》原型模式是一种创建型设计模式,通过克隆现有对象来创建新对象,避免重复的创建成本和复杂的初始化过程,它适用于对象创建过程复杂、需要大量相似对象或避免重复初... 目录什么是原型模式?原型模式的工作原理C#中如何实现原型模式?1. 定义原型接口2. 实现原型接口3

大数据spark3.5安装部署之local模式详解

《大数据spark3.5安装部署之local模式详解》本文介绍了如何在本地模式下安装和配置Spark,并展示了如何使用SparkShell进行基本的数据处理操作,同时,还介绍了如何通过Spark-su... 目录下载上传解压配置jdk解压配置环境变量启动查看交互操作命令行提交应用spark,一个数据处理框架

Java实现状态模式的示例代码

《Java实现状态模式的示例代码》状态模式是一种行为型设计模式,允许对象根据其内部状态改变行为,本文主要介绍了Java实现状态模式的示例代码,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来... 目录一、简介1、定义2、状态模式的结构二、Java实现案例1、电灯开关状态案例2、番茄工作法状态案例

解读静态资源访问static-locations和static-path-pattern

《解读静态资源访问static-locations和static-path-pattern》本文主要介绍了SpringBoot中静态资源的配置和访问方式,包括静态资源的默认前缀、默认地址、目录结构、访... 目录静态资源访问static-locations和static-path-pattern静态资源配置

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

模版方法模式template method

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/template-method 超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。 上层接口有默认实现的方法和子类需要自己实现的方法

【iOS】MVC模式

MVC模式 MVC模式MVC模式demo MVC模式 MVC模式全称为model(模型)view(视图)controller(控制器),他分为三个不同的层分别负责不同的职责。 View:该层用于存放视图,该层中我们可以对页面及控件进行布局。Model:模型一般都拥有很好的可复用性,在该层中,我们可以统一管理一些数据。Controlller:该层充当一个CPU的功能,即该应用程序

迭代器模式iterator

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/iterator 不暴露集合底层表现形式 (列表、 栈和树等) 的情况下遍历集合中所有的元素