【设计模式】 备忘录模式

1、定义

1.1 标准定义

  备忘录模式(Memento Pattern)提供了一种弥补真实世界缺陷的方法,让后悔药在程序的世界中真实可行,其定义如下:
Without violating encapsulation,capture and externalize an object's internal state so that the object can be restored to this state later.(在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。 )

  通俗地说, 备忘录模式就是一个对象的备份模式, 提供了一种程序数据的备份方法 。

1.2 类图

  ● Originator发起人角色
  记录当前时刻的内部状态, 负责定义哪些属于备份范围的状态, 负责创建和恢复备忘录数据。

  ● Memento备忘录角色
  负责存储Originator发起人对象的内部状态, 在需要的时候提供发起人需要的内部状态。

  ● Caretaker备忘录管理员角色
  对备忘录进行管理、 保存和提供备忘录。

2、实现

  Memento模式中封装的是需要保存的状态,当需要恢复的时候才取出来进行恢复.原理很简单,实现的时候需要注意一个地方:窄接口和宽接口.所谓的宽接口就是一般意义上的接口,把对外的接口作为public成员;而窄接口反之,把接口作为private成员,而把需要访问这些接口函数的类作为这个类的友元类,也就是说接口只暴露给了对这些接口感兴趣的类,而不是暴露在外部.下面的实现就是窄实现的方法来实现的.

2.1 类图

  Originator:负责创建一个备忘录Memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态。Originator可根据需要决定Memento存储Originator的哪些内部状态。

  Memento:负责存储Originator对象的内部状态,并可防止Originator以外的其他对象访问备忘录Memento。备忘录有两个接口,Caretaker只能看到备忘录的窄接口,它只能将备忘录传递给其他对象。Originator能够看到一个宽接口,允许它访问返回到先前状态所需的所有数据。

  Caretaker:负责保存好备忘录Memento,不能对备忘录的内容进行操作或检查。

2.2 代码

2.2.1 备忘录

// Memento.h

#ifndef _MEMENTO_H_
#define _MEMENTO_H_
#include <string>

using namespace std;

//负责存储Originator对象的内部状态,并可防止Originator以外的其他对象访问备忘录Memento。
//备忘录有两个接口,Caretaker只能看到备忘录的窄接口,它只能将备忘录传递给其他对象。Originator能够看到一个宽接口,允许它访问返回到先前状态所需的所有数据。
class Memento
{
private:
    //将Originator为friend类,可以访问内部信息,但是其他类不能访问
    friend class Originator;
    Memento(const string& state);
    ~Memento();
    void SetState(const string& state);
    string GetState();
    string _state;
};

//负责创建一个备忘录Memento,用以记录当前时刻它的内部状态,并可使用备忘录恢复内部状态
class Originator
{
public:
    Originator();
    Originator(const string& state);
    ~Originator();
    void RestoreToMemento(Memento* pMemento);
    Memento* CreateMemento();
    void SetState(const string& state);
    string GetState();
    void show();
protected:
private:
    string _state;
};

//负责保存好备忘录Memento,不能对备忘录的内容进行操作或检查
class Caretaker
{
public:
    Caretaker();
    ~Caretaker();
    void SetMemento(Memento*);
    Memento* GetMemento();
private:
    Memento* _memento;
};

#endif
// Memento.cpp

#include "Memento.h"
#include <iostream>
#include <string>

using namespace std;

Memento::Memento(const string& state)
{
    this->_state = state;
}

Memento::~Memento(){}

string Memento::GetState()
{
    return this->_state;
}

void Memento::SetState(const string& state)
{
    this->_state = state;
}

Originator::Originator(){}

Originator::Originator(const string& state)
{
    this->_state = state;
}

Originator::~Originator(){}

string Originator::GetState()
{
    return this->_state;
}

void Originator::show()
{
    cout << this->_state << endl;
}

void Originator::SetState(const string& state)
{
    this->_state = state;
}

Memento* Originator::CreateMemento()
{
    return new Memento(this->_state);
}

void Originator::RestoreToMemento(Memento* pMemento)
{
    this->_state = pMemento->GetState();
}

Caretaker::Caretaker(){}

Caretaker::~Caretaker(){}

Memento* Caretaker::GetMemento()
{
    return this->_memento;
}

void Caretaker::SetMemento(Memento* pMemento)
{
    this->_memento = pMemento;
}

2.2.2 调用

// main.cpp

#include "Memento.h"

int main()
{
    //初始化对象,状态为“Old”
    Originator* o = new Originator("Old");
    o->show();

    //建立并保存Memento
    Caretaker* pTaker = new Caretaker();
    pTaker->SetMemento(o->CreateMemento());

    //改变状态
    o->SetState("New");
    o->show();

    //恢复状态
    o->RestoreToMemento(pTaker->GetMemento());
    o->show();

    return 0;
}

2.2.3 执行结果

3、总结

3.1 优缺点

  由于备忘录模式有太多的变形和处理方式, 每种方式都有它自己的优点和缺点, 标准的备忘录模式很难在项目中遇到, 基本上都有一些变换处理方式。 因此, 我们在使用备忘录模式时主要了解如何应用以及需要注意哪些事项就成了。

3.2 使用场景

  ● 需要保存和恢复数据的相关状态场景。
  ● 提供一个可回滚( rollback) 的操作; 比如Word中的CTRL+Z组合键, IE浏览器中的后退按钮, 文件管理器上的backspace键等。
  ● 需要监控的副本场景中。 例如要监控一个对象的属性, 但是监控又不应该作为系统的主业务来调用, 它只是边缘应用, 即使出现监控不准、 错误报警也影响不大, 因此一般的做法是备份一个主线程中的对象, 然后由分析程序来分析。
  ● 数据库连接的事务管理就是用的备忘录模式。

 3.3 注意事项

  ● 备忘录的生命期
  备忘录创建出来就要在最近的代码中使用,要主动管理它的生命周期,建立就要使用,不使用就要立刻删除其引用,等待垃圾回收器对它的回收处理。

  ● 备忘录的性能
  不要在频繁建立备份的场景中使用备忘录模式(比如一个for循环中),原因有二:一是控制不了备忘录建立的对象数量;二是大对象的建立是要消耗资源的, 系统的性能需要考虑。因此,如果出现这样的代码,设计师就应该好好想想怎么修改架构了。 

posted @ 2017-08-07 21:18  Memset  阅读(219)  评论(0编辑  收藏  举报