备忘录保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式现在有内存序列化,内存编码等方式较好的替换。
1. 介绍
意图:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
主要解决:所谓备忘录模式就是在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样可以在以后将对象恢复到原先保存到额状态。
何时使用:很多时候我们总是需要记录一个对象的内部状态,这样做的目的就是为了允许用户取消不确定或者错误的操作,能够恢复到他原先的状态该,是的他有“后悔药”可吃。
如何解决:通过一个备忘录类专门存储对象状态。
关键代码:客户不与备忘录耦合,与备忘录管理类耦合。
应用实例:1、后悔药。2、打游戏时的存档。3、window里的ctrl+z。4、IE中的后退。5、数据库的事务管理。
优点:1、给用户提供一种可以恢复状态的机制,可以使用用户能够比较方便的回到某个历史的状态。2、实现了信息的封装,使得用户不需要关心状态的保存细节。
缺点:消耗资源。如果累的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
使用场景1、需要保存/恢复数据的相关状态场景。2、提供一个可回滚的操作。
注意事项:1、为了符合迪米特原则,还要增加一个管理备忘录的类。2、为了节约内存,可使用原形模式+备忘录模式。
2. 实例
主要角色如下:
备忘录角色(Memento): 负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人;
发起人角色(Originator): 记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,可以根据需要决定备忘录角色中保存自身的哪些内部状态,能够访问备忘录里的所有信息;
管理者角色(Caretaker): 对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改,只能够将备忘录传递给其他对象。
备忘录模式使用三个类 Memento、Originator 和 CareTaker。Memento 包含了要被恢复的对象的状态。Originator 创建并在 Memento 对象中存储状态。Caretaker 对象负责从 Memento 中恢复对象的状态。
MementoPatternDemo,我们的演示类使用 CareTaker 和 Originator 对象来显示对象的状态恢复。
2.1. C++实现
#include <stdio.h>
#include <memory>
#include <vector>
#include <map>
typedef struct {
int nHp;
int nMp;
int nAttack;
}GameValue_t;
class Memento {
public:
Memento(GameValue_t gameValue) : m_gameValue(gameValue){}
const GameValue_t const getGameValue ()
{
return m_gameValue;
}
private:
GameValue_t m_gameValue;
};
class CareTaker {
public:
std::shared_ptr< Memento > GetCareState(std::string desc)
{
return m_takerMap[desc];
}
void SetCareState(std::string desc, std::shared_ptr< Memento> memto)
{
m_takerMap[desc] = memto;
}
private:
std::map<std::string, std::shared_ptr<Memento>> m_takerMap;
};
class Hero {
public:
Hero() : m_gameValue{ 10, 10, 100 }{}
void SaveCurrentGameState(std::string desc)
{
taker.SetCareState(desc, std::make_shared<Memento>(m_gameValue));
}
void ResumeGameState(std::string desc)
{
m_gameValue = taker.GetCareState(desc)->getGameValue();
}
void Attach(){
m_gameValue.nHp = rand() % 10;
m_gameValue.nMp = rand() % 10;
m_gameValue.nAttack = rand() % 100;
}
void ShowGameValue() {
std::cout << "Current languang is: " << m_gameValue.nHp << std::endl;
std::cout << "Current xueliang is: " << m_gameValue.nMp << std::endl;
std::cout << "Currnet attach is: " << m_gameValue.nAttack << std::endl;
}
private:
GameValue_t m_gameValue;
CareTaker taker;
};
#include <stdio.h>
#include <iostream>
#include <string>
#include "game.h"
int main(int argc, char** argv)
{
Hero hero;
hero.ShowGameValue();
hero.SaveCurrentGameState("start");
hero.Attach();
hero.ShowGameValue();
hero.SaveCurrentGameState("attach1");
hero.Attach();
hero.ShowGameValue();
hero.ResumeGameState("start");
hero.ShowGameValue();
system("pause");
return 0;
}
3. 小结
备忘录模式与原型模式:原型模式也能保存一个对象在某一个时刻的状态,那么两者有何不同的呢?原型模式保存的是当前对象的所有状态信息,恢复的时候会生成与保存的对象完全相同的另外一个实例;而备忘录模式保存的是我们关心的在恢复时需要的对象的部分状态信息,相当于快照。