Memento模式详解--设计模式(16)

Memento模式来源:

     我们在进行软件系统的设计时候是要给用户后悔的权利(实际上可能也是用户要求的权利:)),我们对一些关键性的操作肯定需要提供诸如撤销(Undo)的操作。那这个后悔药就是Memento模式提供的。

Memento模式作用:

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

Memento模式UML结构图如图1所示:

                     

Memento模式构成:

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

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

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

Memento模式代码示例:

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</span>

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;
}</span>

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;
}</span>

Memento模式的适用性:

(1).Memento模式比较适用于功能比较复杂的,但需要维护或记录历史属性的类,或者需要保存的属性只是众多属性中的一小部分时,Originator可以根据保存的Memento信息还原到前一状态。如果在某个系统中使用命令模式时,需要实现命令的撤销功能,那么命令模式可以使用备忘录模式来存储可撤销操作的状态。

Memento模式的优缺点:

优点:

(1).当发起人角色的状态有改变时,有可能是个错误的改变,我们使用备忘录模式就可以把这个错误改变还原。

(2).备份的状态是保存在发起人角色之外的,这样,发起人角色就不需要对各个备份的状态进行管理。

缺点:

(1).如果备份的对象存在大量的信息或者创建、恢复操作非常频繁,则可能造成很大的性能开销。

     Memento模式的使用总结:Memento模式的关键就是要在不破坏封装行的前提下,捕获并保存一个类的内部状态,这样就可以利用该保存的状态实施恢复操作。为了达到这个目标,可以在后面的实现中看到我们采取了一定语言支持的技术

Memento模式的关键就是friend class Originator;我们可以看到,Memento的接口都声明为private,而将Originator声明为Memento的友元类。我们将Originator的状态保存在Memento类中,而将Memento接口private起来,也就达到了封装的功效。

在Originator类中我们提供了方法让用户后悔:RestoreToMemento(Memento* mt);我们可以通过这个接口让用户后悔。在示例程序中,我们演示了这一点:Originator的状态由old变为new最后又回到了old。

时间: 2024-09-28 20:55:46

Memento模式详解--设计模式(16)的相关文章

Command模式详解--设计模式(18)

Memento模式来源:       Command模式通过将请求封装到一个对象(Command)中,并将请求的接受者存放到具体的ConcreteCommand类中(Receiver)中,从而实现调用操作的对象和操作的具体实现者之间的解耦. Memento模式作用: 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化:对请求排队或记录请求日志,以及支持可撤销的操作.由于"行为请求者"与"行为实现者"的紧耦合,使用命令模式,可以对请求排队或记录请求日志,

Proxy模式详解--设计模式(11)

Proxy模式的产生原因:        在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 当由于某些特定的需要调用的对象在另外一台机器上,需要跨越网络才能访问,在没有WebService的情况下我们需要直接coding去处理网络连接.处理打包.解包等等非常复杂的步骤,而WebService的出现帮我们解决了其中的一些问题简化客户端的处理,我们只需在客户端建立一个远程对象的代理,客户端就象调用本地对象一样调用该代理,再由代理去跟实际对象联

Interpreter 模式详解--设计模式(22)

Interpreter 模式的来源: Interpreter(解释器)模式是一种特殊的设计模式,它建立一个解释器(Interpreter),对于特定的计算机程序设计语言,用来解释预先定义的文法.简单地说,Interpreter模式是一种简单的语法解释器构架.解释器模式属于行为模式,给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子. Interpreter 模式作用:     正如其名,此模式大多用来解释一些(自定义的)独特语法,例如某些游戏开发引擎中

Observer模式详解--设计模式(15)

Observer模式来源: Observer模式应该可以说是应用最多.影响最广的模式之一. 因为Observer的一个实例Model/View/Control(MVC)结构在系统开发架构设计中有着很重要的地位和意义,MVC实现了业务逻辑和表示层的解耦.在MFC中,Doc/View(文档视图结构)提供了实现MVC的框架结构(有一个从设计模式(Observer模式)的角度分析分析Doc/View的文章正在进一步的撰写当中,遗憾的是时间:)).在Java阵容中,Struts则提供和MFC中Doc/Vi

Strategy模式详解--设计模式(13)

Strategy模式来源:        在软件开发中也常常遇到类似的情况,实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能.如查找.排序等,一种常用的方法是硬编码(Hard Coding)在一个类中,如需要提供多种查找算法,可以将这些算法写到一个类中,在该类中提供多个方法,每一个方法对应一个具体的查找算法:当然也可以将这些查找算法封装在一个统一的方法中,通过if-else-或者case等条件判断语句来进行选择.这两种实现方法我们都可以称之为硬编

C++Builder建造者模式详解--设计模式(4)

生活中有着很多的Builder的例子,个人觉得大学生活就是一个Builder模式的最好体验:要完成大学教育,一般将大学教育过程分成4个学期进行,因此没有学习可以看作是构建完整大学教育的一个部分构建过程,每个人经过这4年的(4个阶段)构建过程得到的最后的结果不一样,因为可能在四个阶段的构建中引入了很多的参数(每个人的机会和际遇不完全相同). Builder模式要解决的也正是这样的问题:当我们要创建的对象很复杂的时候(通常是由很多其他的对象组合而成),我们要要复杂对象的创建过程和这个对象的表示(展示

Template模式详解--设计模式(12)

Template模式来源:    在面向对象系统的分析与设计过程中经常会遇到这样一种情况:对于某一个业务逻辑(算法实现)在不同的对象中有不同的细节实现,但是逻辑(算法)的框架(或通用的应用算法)是相同的.Template提供了这种情况的一个实现框架. Template模式作用: Template模式又叫模板方法模式,在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以在不改变算法结构的情冴下,重新定义算法中的某些步骤. 我们使用冲泡咖啡和冲泡茶的例子 加工流程: 咖啡冲

State模式详解--设计模式(14)

State模式来源:         每个人.事物在不同的状态下会有不同表现(动作),而一个状态又会在不同的表现下转移到下一个不同的状态(State).最简单的一个生活中的例子就是:地铁入口处,如果你放入正确的地铁票,门就会打开让你通过.在出口处也是验票,如果正确你就可以ok,否则就不让你通过(如果你动作野蛮,或许会有报警(Alarm)). 有限状态自动机(FSM)也是一个典型的状态不同,对输入有不同的响应(状态转移).通常我们在实现这类系统会使用到很多的Switch/Case语句,Case某种

Iterator模式详解--设计模式(21)

Iterator模式来源: 迭代器(Iterator)模式,又叫做游标(Cursor)模式.GOF给出的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节.从定义可见,迭代器模式是为容器而生. Iterator模式作用: (1).它支持以不同的方式遍历一个聚合复杂的聚合可用多种方式进行遍历,如二叉树的遍历,可以采用前序.中序或后序遍历.迭代器模式使得改变遍历算法变得很容易: 仅需用一个不同的迭代器的实例代替原先的实例即可,你也可以自己定义迭代器的子