.Net行为型设计模式之备忘录模式(Memento)

目录
  • 一、动机(Motivate)
  • 二、意图(Intent)
  • 三、结构图(Structure)
  • 四、模式的组成
  • 五、备忘录模式的代码实现
  • 六、备忘录模式的实现要点:
    • 1、备忘录模式的主要优点有:
    • 2、备忘录模式的主要缺点有:
    • 3、在下面的情况下可以考虑使用备忘录模式:
    • 4、备忘录的封装性
    • 5、多备份实现
    • 6、Memento模式与Command模式的异同
  • 七、.NET 备忘录模式的实现

一、动机(Motivate)

我们看上图,一个对象肯定会有很多状态,这些状态肯定会相互转变而促进对象的发展,如果要想在某一时刻把当前对象回复到以前某一时刻的状态,这个情况用“备忘录模式”就能很好解决该问题。

在软件构建过程中,某些对象的状态在转换的过程中,可能由于某种需要,要求程序能够回溯到对象之前处于某个点时的状态。如果使用一些公有接口来让其他对象得到对象的状态,便会暴露对象的细节实现。
如何实现对象状态的良好保存与恢复,但同时又不会因此而破坏对象本身的封装性?

二、意图(Intent)

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态(如果没有这个关键点,其实深拷贝就可以解决问题)。这样以后就可以将该对象恢复到原先保存的状态。                                 ——《设计模式》GoF

三、结构图(Structure)

四、模式的组成

可以看出,在备忘录模式的结构图有以下角色:
(1)、发起人角色(Originator):记录当前时刻的内部状态,负责创建和恢复备忘录数据。负责创建一个备忘录Memento,用以记录当前时刻自身的内部状态,并可使用备忘录恢复内部状态。Originator【发起人】可以根据需要决定Memento【备忘录】存储自己的哪些内部状态。
(2)、备忘录角色(Memento):负责存储发起人对象的内部状态,在进行恢复时提供给发起人需要的状态,并可以防止Originator以外的其他对象访问备忘录。备忘录有两个接口:Caretaker【管理角色】只能看到备忘录的窄接口,他只能将备忘录传递给其他对象。Originator【发起人】却可看到备忘录的宽接口,允许它访问返回到先前状态所需要的所有数据。
(3)、管理者角色(Caretaker):负责保存备忘录对象。负责备忘录Memento,不能对Memento的内容进行访问或者操作。

五、备忘录模式的代码实现

今天我们就用备份电话本的实例来说明备忘录模式的实现。实现代码如下:

static void Main(string[] args)
{
    List<ContactPerson> persons = new List<ContactPerson>()
            {
                new ContactPerson() { Name="黄飞鸿", MobileNumber = "13533332222"},
                new ContactPerson() { Name="方世玉", MobileNumber = "13966554433"},
                new ContactPerson() { Name="洪熙官", MobileNumber = "13198765544"}
            };

    //手机名单发起人
    MobileBackOriginator mobileOriginator = new MobileBackOriginator(persons);
    mobileOriginator.Show();

    // 创建备忘录并保存备忘录对象
    MementoManager manager = new MementoManager();
    manager.ContactPersonMemento = mobileOriginator.CreateMemento();

    // 更改发起人联系人列表
    Console.WriteLine("----移除最后一个联系人--------");
    mobileOriginator.ContactPersonList.RemoveAt(2);
    mobileOriginator.Show();

    // 恢复到原始状态
    Console.WriteLine("-------恢复联系人列表------");
    mobileOriginator.RestoreMemento(manager.ContactPersonMemento);
    mobileOriginator.Show();

}
// 联系人--需要备份的数据,是状态数据,没有操作
public sealed class ContactPerson
{
    //姓名
    public string Name { get; set; }

    //电话号码
    public string MobileNumber { get; set; }
}

// 发起人--相当于【发起人角色】Originator
public sealed class MobileBackOriginator
{
    // 发起人需要保存的内部状态
    private List<ContactPerson> _personList;

    public List<ContactPerson> ContactPersonList
    {
        get
        {
            return this._personList;
        }

        set
        {
            this._personList = value;
        }
    }
    //初始化需要备份的电话名单
    public MobileBackOriginator(List<ContactPerson> personList)
    {
        if (personList != null)
        {
            this._personList = personList;
        }
        else
        {
            throw new ArgumentNullException("参数不能为空!");
        }
    }

    // 创建备忘录对象实例,将当期要保存的联系人列表保存到备忘录对象中
    public ContactPersonMemento CreateMemento()
    {
        return new ContactPersonMemento(new List<ContactPerson>(this._personList));
    }

    // 将备忘录中的数据备份还原到联系人列表中
    public void RestoreMemento(ContactPersonMemento memento)
    {
        this.ContactPersonList = memento.ContactPersonListBack;
    }

    public void Show()
    {
        Console.WriteLine("联系人列表中共有{0}个人,他们是:", ContactPersonList.Count);
        foreach (ContactPerson p in ContactPersonList)
        {
            Console.WriteLine("姓名: {0} 号码: {1}", p.Name, p.MobileNumber);
        }
    }
}

// 备忘录对象,用于保存状态数据,保存的是当时对象具体状态数据--相当于【备忘录角色】Memeto
public sealed class ContactPersonMemento
{
    // 保存发起人创建的电话名单数据,就是所谓的状态
    public List<ContactPerson> ContactPersonListBack { get; private set; }

    public ContactPersonMemento(List<ContactPerson> personList)
    {
        ContactPersonListBack = personList;
    }
}

// 管理角色,它可以管理【备忘录】对象,如果是保存多个【备忘录】对象,当然可以对保存的对象进行增、删等管理处理---相当于【管理者角色】Caretaker
public sealed class MementoManager
{
    //如果想保存多个【备忘录】对象,可以通过字典或者堆栈来保存,堆栈对象可以反映保存对象的先后顺序
    //比如:public Dictionary<string, ContactPersonMemento> ContactPersonMementoDictionary { get; set; }
    public ContactPersonMemento ContactPersonMemento { get; set; }
}

六、备忘录模式的实现要点:

备忘录(Memento)存储原发器(Originator)对象的内部状态,在需要时恢复原发器状态。Memento模式适用于“由原发器管理,却又必须存储在原发器之外的信息”。
在实现Memento模式中,要防止原发器以外的对象访问备忘录对象。备忘录对象有两个接口,一个为原发器使用的宽接口;一个为其他对象使用的窄接口。在实现Memento模式时,要考虑拷贝对象状态的效率问题,如果对象开销比较大,可以采用某种增量式改变(即只记住改变的状态)来改进Memento模式。
我们也可以用序列化的方式实现备忘录。序列化之后,我们可以把它临时性保存到数据库、文件、进程内、进程外等地方。

1、备忘录模式的主要优点有:

1】、如果某个操作错误地破坏了数据的完整性,此时可以使用备忘录模式将数据恢复成原来正确的数据。
2】、备份的状态数据保存在发起人角色之外,这样发起人就不需要对各个备份的状态进行管理。而是由备忘录角色进行管理,而备忘录角色又是由管理者角色管理,符合单一职责原则。
3】、提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无效或者存在问题时,可以使用先前存储起来的备忘录将状态复原。
4】、实现了信息的封装,一个备忘录对象是一种原发器对象的表示,不会被其他代码改动,这种模式简化了原发器对象,备忘录只保存原发器的状态,采用堆栈来存储备忘录对象可以实现多次撤销操作,可以通过在负责人中定义集合对象来存储多个备忘录。
5】、本模式简化了发起人类。发起人不再需要管理和保存其内部状态的一个个版本,客户端可以自行管理他们所需要的这些状态的版本。
6】、当发起人角色的状态改变的时候,有可能这个状态无效,这时候就可以使用暂时存储起来的备忘录将状态复原。

2、备忘录模式的主要缺点有:

1】、在实际的系统中,可能需要维护多个备份,需要额外的资源,这样对资源的消耗比较严重。资源消耗过大,如果类的成员变量太多,就不可避免占用大量的内存,而且每保存一次对象的状态都需要消耗内存资源,如果知道这一点大家就容易理解为什么一些提供了撤销功能的软件在运行时所需的内存和硬盘空间比较大了。
2】、如果发起人角色的状态需要完整地存储到备忘录对象中,那么在资源消耗上面备忘录对象会很昂贵。
3】、当负责人角色将一个备忘录 存储起来的时候,负责人可能并不知道这个状态会占用多大的存储空间,从而无法提醒用户一个操作是否很昂贵。
4】、当发起人角色的状态改变的时候,有可能这个协议无效。如果状态改变的成功率不高的话,不如采取“假如”协议模式。

3、在下面的情况下可以考虑使用备忘录模式:

1】、如果系统需要提供回滚操作时,使用备忘录模式非常合适。例如文本编辑器的Ctrl+Z撤销操作的实现,数据库中事务操作。
2】、保存一个对象在某一个时刻的状态或部分状态,这样以后需要时它能够恢复到先前的状态。
3】、如果用一个接口来让其他对象得到这些状态,将会暴露对象的实现细节并破坏对象的封装性,一个对象不希望外界直接访问其内部状态,通过负责人可以间接访问其内部状态。
4】、有时一些发起人对象的内部信息必须保存在发起人对象以外的地方,但是必须要由发起人对象自己读取,这时,使用备忘录模式可以把复杂的发起人内部信息对其他的对象屏蔽起来,从而可以恰当地保持封装的边界。

4、备忘录的封装性

为了确保备忘录的封装性,除了原发器外,其他类是不能也不应该访问备忘录类的,在实际开发中,原发器与备忘录之间的关系是非常特殊的,它们要分享信息而不让其他类知道,实现的方法因编程语言的不同而不同。

5、多备份实现

1】、在负责人中定义一个集合对象来存储多个状态,而且可以方便地返回到某一历史状态。
2】、在备份对象时可以做一些记号,这些记号称为检查点(Check Point)。在使用HashMap等实现时可以使用Key来设置检查点。

6、Memento模式与Command模式的异同

Memento备忘录模式和Command命令模式其实还是有些细微的差别的,那就让我们来看看他们的异同吧。虽然两者都支持Undo操作,但是Command是对行为的封装,Memento是对对象状态的保留,这是目的上的不同。它们支持的也是Undo操作的不同层面,Command是对行为序列的操作,Memento是对行为状态的操作。命令模式保存的是发起人的具体命令(命令对应的是行为),而备忘录模式保存的是发起人的状态(而状态对应的数据结构,如属性)。把握细节,理解模式的应用场景,这样可以让模式更好的为我们服务。

七、.NET 备忘录模式的实现

在现在的Net框架里面,还没有找到备忘录模式的实现,看来还是自己的功力不够,还需努力。个人的理解,这种模式似乎在业务系统里面使用的更多,类似Word,Excel等工具可以有撤销功能,其实很多软件都有这个功能,软件执行的时候,时时刻刻在把自己的状态存储,如果发生错误,或者需要撤销的时候就可以进行相关的操作。

到此这篇关于.Net行为型设计模式之备忘录模式(Memento)的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • .Net行为型设计模式之中介者模式(Mediator)

    目录 一.动机(Motivate) 二.意图(Intent) 三.结构图(Structure) 四.模式的组成 五.中介者模式的代码实现 六.中介者模式的实现要点: 1.中介者模式的优点 2.中介者模式的缺点 七..NET 中介者模式的实现 一.动机(Motivate) 为什么要使用中介者模式呢?如果不使用中介者模式的话,各个同事对象将会相互进行引用,如果每个对象都与多个对象进行交互时,将会形成如下图所示的网状结构. 从上图可以发现,如果不使用中介者模式的话,每个对象之间过度耦合,这样的既不利于

  • .Net行为型设计模式之迭代器模式(Iterator)

    目录 一.动机(Motivate) 二.意图(Intent) 三.结构图 四.模式的组成 五.迭代器模式的代码实现 六.迭代器模式的实现要点: 迭代器模式的优点: 迭代器模式的缺点: 迭代器模式的使用场景: 七..NET 中迭代器模式的实现 一.动机(Motivate) 在软件构建过程中,集合对象内部结构常常变化各异.但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码透明地访问其中包含的元素:同时这种“透明遍历”也为“同一种算法在多种集合对象上进行操作”提供了可能.使用面向

  • .Net行为型设计模式之策略模式(Stragety)

    目录 一.动机(Motivate) 二.意图(Intent) 三.结构图(Structure) 四.模式的组成 五.策略模式的代码实现 六.策略模式的实现要点: 1.策略模式的主要优点有: 2.策略模式的主要缺点有: 3.在下面的情况下可以考虑使用策略模式: 七..NET 策略模式的实现 一.动机(Motivate) 在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂:而且有时候支持不使用的算法也是一个性能负担.如何在运行时根据需要透

  • .Net行为型设计模式之模板方法模式(Template Method)

    一.动机(Motivate) “模板方法”,就是有一个方法包含了一个模板,这个模板是一个算法.在我们的现实生活中有很多例子可以拿来说明这个模式,就拿吃饺子这个事情来说,要想吃到饺子必须经过三步,第一步是“和面”,第二步是“包馅”,第三步是“煮饺子”,这三步就是一个算法,我们要想吃到不同的面和馅的饺子,对这三步中的任意一步就行操作就可以,也可以完全定义这三步 在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因(比如框架与应用之间的关系)

  • .Net行为型设计模式之观察者模式(Observer)

    目录 一.动机(Motivate) 二.意图(Intent) 三.结构图 四.模式的组成 五.观察者模式的代码实现 六.观察者模式的实现要点: 1.观察者模式的优点: 2.观察者模式的缺点: 七..NET 中观察者模式的实现 一.动机(Motivate) “观察者模式”在现实生活中,实例其实是很多的,比如:八九十年代我们订阅的报纸,我们会定期收到报纸,因为我们订阅了.银行可以给储户发手机短信,也是“观察者模式”很好的使用的例子,因为我们订阅了银行的短信业务,当我们账户余额发生变化就会收到通知等.

  • .Net行为型设计模式之命令模式(Command)

    目录 一.动机(Motivate) 二.意图(Intent) 三.结构图(Structure) 四.模式的组成 五.命令模式的代码实现 六.命令模式的实现要点: 1.命令模式的优点: 2.命令模式的缺点: 3.命令模式的使用场景: 七..NET 中命令模式的实现 一.动机(Motivate) 在我们的现实生活中有很多例子可以拿来说明这个模式,我们还拿吃饺子这个事情来说.我的奶奶说了,今天想吃饺子,发出了命令,然后我奶奶就去看电视去了.我们夫妻俩收到命令就开始和面,做饺子馅,包饺子.饺子包好了,我

  • .Net行为型设计模式之状态模式(State)

    目录 一.动机(Motivate) 二.意图(Intent) 三.结构图(Structure) 四.模式的组成 五.状态模式的代码实现 六.状态模式的实现要点: 1.状态模式的优点 2.状态模式的缺点 3.在以下情况下可以使用状态模式: 七..NET 状态模式的实现 一.动机(Motivate) 在软件构建过程中,某些对象的状态如果改变,其行为也会随之而发生变化,比如文档处于只读状态,其支持的行为和读写状态支持的行为就可能完全不同.如何在运行时根据对象的状态来透明地更改对象的行为?而不会为对象操

  • .Net行为型设计模式之访问者模式(Visitor)

    目录 一.动机(Motivate) 二.意图(Intent) 三.结构图(Structure) 四.模式的组成 五.访问者模式的代码实现 六.访问者模式的实现要点: (1).访问者模式的主要优点有: (2).访问者模式的主要缺点有: (3).在下面的情况下可以考虑使用访问者模式: 七..NET 访问者模式的实现 一.动机(Motivate) 在软件构建过程中,由于需求的改变,某些类层次结构中常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设

  • .Net行为型设计模式之职责链模式(Chain of Responsibility)

    目录 一.动机(Motivate) 二.意图(Intent) 三.结构图(Structure) 四.模式的组成 五.职责链模式的代码实现 六.职责链模式的实现要点: 1.职责链模式的主要优点有: 2.职责链模式的主要缺点有: 3.在下面的情况下可以考虑使用职责链模式: 七..NET 职责链模式的实现 一.动机(Motivate) 在软件构建过程中,一个请求可能被多个对象处理,但是每个请求在运行时只能有一个接受者,如果显示指定,将必不可少地带来请求发送者与接受者的紧耦合.如何使请求的发送者不需要指

  • .Net行为型设计模式之备忘录模式(Memento)

    目录 一.动机(Motivate) 二.意图(Intent) 三.结构图(Structure) 四.模式的组成 五.备忘录模式的代码实现 六.备忘录模式的实现要点: 1.备忘录模式的主要优点有: 2.备忘录模式的主要缺点有: 3.在下面的情况下可以考虑使用备忘录模式: 4.备忘录的封装性 5.多备份实现 6.Memento模式与Command模式的异同 七..NET 备忘录模式的实现 一.动机(Motivate) 我们看上图,一个对象肯定会有很多状态,这些状态肯定会相互转变而促进对象的发展,如果

  • C++设计模式之备忘录模式(Memento)

    当我们在实际应用中需要提供撤销机制,当一个对象可能需要再后续操作中恢复其内部状态时,就需要使用备忘录模式.其本质就是对象的序列化和反序列化的过程,支持回滚操作. 作用 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以将该对象恢复到原先的状态. 类视图 实现 typedef struct sysstate; //假设的一个空结构,用来代表系统状态 //还原点 class Memento { public: Memento(sysstate &statein

  • Java设计模式之备忘录模式_动力节点Java学院

    定义:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样就可以将该对象恢复到原先保存的状态 类型:行为类 类图: 我们在编程的时候,经常需要保存对象的中间状态,当需要的时候,可以恢复到这个状态.比如,我们使用Eclipse进行编程时,假如编写失误(例如不小心误删除了几行代码),我们希望返回删除前的状态,便可以使用Ctrl+Z来进行返回.这时我们便可以使用备忘录模式来实现. 备忘录模式的结构 发起人:记录当前时刻的内部状态,负责定义哪些属于备份范围的状态,负责创建和

  • JAVA设计模式之备忘录模式原理与用法详解

    本文实例讲述了JAVA设计模式之备忘录模式.分享给大家供大家参考,具体如下: 备忘录模式:又叫做快照模式,指在不破坏封装性的前提下,获取到一个对象的内部状态,并在对象之外记录或保存这个状态.在有需要的时候可将该对象恢复到原先保存的状态.我们相当于把对象原始状备份保留,所以叫备忘录模式. *模式 角色对象组成: 1.发起者对象:负责创建一个备忘录来记录当前对象的内部状态,并可使用备忘录恢复内部状态. 2.备忘录对象:负责存储发起者对象的内部状态,并防止其他对象访问备忘录. 3.管理者对象:负责备忘

  • Android编程设计模式之备忘录模式详解

    本文实例讲述了Android编程设计模式之备忘录模式.分享给大家供大家参考,具体如下: 一.介绍 备忘录模式是一种行为模式,该模式用于保存对象当前状态,并且在之后可以再次恢复到此状态,这有点像我们平时说的"后悔药".备忘录模式实现的方式需要保证被保存的对象状态不能被对象从外部访问,目的是为了保护好被保存的这些对象状态的完整性以及内部实现不向外暴露. 二.定义 在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样,以后就可将该对象恢复到原先保存的状态. 三.使用

  • Python设计模式之备忘录模式原理与用法详解

    本文实例讲述了Python设计模式之备忘录模式原理与用法.分享给大家供大家参考,具体如下: 备忘录模式(Memento Pattern):不破坏封装性的前提下捕获一个对象的内部状态,并在该对象之外保存这个状态,这样已经后就可将该对象恢复到原先保存的状态 下面是一个备忘录模式的demo: #!/usr/bin/env python # -*- coding:utf-8 -*- __author__ = 'Andy' """ 大话设计模式 设计模式--备忘录模式 备忘录模式(Me

  • 实例讲解JAVA设计模式之备忘录模式

    在讲述这个模式之前,我们先看一个案例:游戏回档 游戏的某个场景,一游戏角色有生命力.攻击力.防御力等数据,在打Boss前和后会不一样,我们允许玩家如果感觉与Boss决斗的效果不理想,可以让游戏恢复到决斗前.下面是代码: 游戏角色类,用来存储角色的生命力.攻击力.防御力的数据. public class GameRole { private int vit;//生命力 private int atk;//攻击力 private int def;//防御力 //状态显示 public void st

  • C#备忘录模式(Memento Pattern)实例教程

    本文以一个简单实例讲述了C#备忘录模式(Memento Pattern)的实现方法.分享给大家供大家参考.具体实现方法如下: 简单来说,备忘录模式就是支持回退操作.假设让一个Notepad支持回退操作,如何实现呢? 首先需要一个备忘录类. public class Memento { private string _msg; public Memento(string msg) { _msg = msg; } public string GetText() { return _msg; } }

  • Java 深入理解创建型设计模式之原型模式

    1.思考问题 现在有一只羊 tom,姓名为: tom,年龄为:1,颜色为:白色,请编写程序创建和 tom羊属性完全相同的10只羊. 按照传统的思路来,我们可能会按照下面的方式去写. 那么这种写法的优缺点自然而然就出来了: 优点是比较好理解,简单易操作. 缺点是在创建新的对象时,总是需要重新获取原始对象的属性,如果创建的对象比较复杂时,效率较低.总是需要重新初始化对象,而不是动态地获得对象运行时的状态,不够灵活. 改进的思路分析:Java中Object类是所有类的根类,Object类提供了一个 c

  • Java 深入理解创建型设计模式之建造者模式

    1.提出问题 假如说,我们需要建房子:这一过程为打桩.砌墙.封顶.房子有各种各样的,比如普通房,高楼,别墅,各种房子的过程虽然一样,但是要求不要相同的.3)请编写程序,完成需求. 传统的想法应该就是下面这个类图的形式.. 那么这种写法的优点就是 比较好理解,简单易操作. 缺点则是:设计的程序结构,过于简单,没有设计缓存层对象,程序的扩展和维护不好.也就是说,这种设计方案,把产品(即: 房子)和创建产品的过程(即: 建房子流程)封装在一起,耦合性增强了. 解决方案:  将产品和产品建造过程解耦 

随机推荐