C#设计模式(23)——备忘录模式(Memento Pattern)

一、引言

  在上一篇博文分享了访问者模式,访问者模式的实现是把作用于某种数据结构上的操作封装到访问者中,使得操作和数据结构隔离。而今天要介绍的备忘者模式与命令模式有点相似,不同的是,命令模式保存的是发起人的具体命令(命令对应的是行为),而备忘录模式保存的是发起人的状态(而状态对应的数据结构,如属性)。下面具体来看看备忘录模式。

二、备忘录模式介绍

2.1 备忘录模式的定义

  从字面意思就可以明白,备忘录模式就是对某个类的状态进行保存下来,等到需要恢复的时候,可以从备忘录中进行恢复。生活中这样的例子经常看到,如备忘电话通讯录,备份操作操作系统,备份数据库等。

  备忘录模式的具体定义是:在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可以把该对象恢复到原先的状态。

2.2 备忘录模式的结构图

  介绍完备忘录模式的定义之后,下面具体看看备忘录模式的结构图:

  备忘录模式中主要有三类角色:

  • 发起人角色:记录当前时刻的内部状态,负责创建和恢复备忘录数据。
  • 备忘录角色:负责存储发起人对象的内部状态,在进行恢复时提供给发起人需要的状态。
  • 管理者角色:负责保存备忘录对象。

2.3 备忘录模式的实现

  下面以备份手机通讯录为例子来实现了备忘录模式,具体的实现代码如下所示:

 1 namespace MementoPattern
 2 {
 3     // 联系人
 4     public class ContactPerson
 5     {
 6         public string Name { get; set; }
 7         public string MobileNum { get; set; }
 8     }
 9
10     // 发起人
11     public class MobileOwner
12     {
13         // 发起人需要保存的内部状态
14         public List<ContactPerson> ContactPersons { get; set; }
15
16         public MobileOwner(List<ContactPerson> persons)
17         {
18             ContactPersons = persons;
19         }
20
21         // 创建备忘录,将当期要保存的联系人列表导入到备忘录中
22         public ContactMemento CreateMemento()
23         {
24             return new ContactMemento(new List<ContactPerson>(this.ContactPersons));25         }
26
27         // 将备忘录中的数据备份导入到联系人列表中
28         public void RestoreMemento(ContactMemento memento)
29         {
30             this.ContactPersons = memento.contactPersonBack;
31         }
32
33         public void Show()
34         {
35             Console.WriteLine("联系人列表中有{0}个人,他们是:", ContactPersons.Count);
36             foreach (ContactPerson p in ContactPersons)
37             {
38                 Console.WriteLine("姓名: {0} 号码为: {1}", p.Name, p.MobileNum);
39             }
40         }
41     }
42
43     // 备忘录
44     public class ContactMemento
45     {
46         // 保存发起人的内部状态
47         public List<ContactPerson> contactPersonBack;
48
49         public ContactMemento(List<ContactPerson> persons)
50         {
51             contactPersonBack = persons;
52         }
53     }
54
55     // 管理角色
56     public class Caretaker
57     {
58         public ContactMemento ContactM { get; set; }
59     }
60
61     class Program
62     {
63         static void Main(string[] args)
64         {
65             List<ContactPerson> persons = new List<ContactPerson>()
66             {
67                 new ContactPerson() { Name= "Learning Hard", MobileNum = "123445"},
68                 new ContactPerson() { Name = "Tony", MobileNum = "234565"},
69                 new ContactPerson() { Name = "Jock", MobileNum = "231455"}
70             };
71             MobileOwner mobileOwner = new MobileOwner(persons);
72             mobileOwner.Show();
73
74             // 创建备忘录并保存备忘录对象
75             Caretaker caretaker = new Caretaker();
76             caretaker.ContactM = mobileOwner.CreateMemento();
77
78             // 更改发起人联系人列表
79             Console.WriteLine("----移除最后一个联系人--------");
80             mobileOwner.ContactPersons.RemoveAt(2);
81             mobileOwner.Show();
82
83             // 恢复到原始状态
84             Console.WriteLine("-------恢复联系人列表------");
85             mobileOwner.RestoreMemento(caretaker.ContactM);
86             mobileOwner.Show();
87
88             Console.Read();
89         }
90     }
91 }

  具体的运行结果如下图所示:

  从上图可以看出,刚开始通讯录中有3个联系人,然后移除以后一个后变成2个联系人了,最后恢复原来的联系人列表后,联系人列表中又恢复为3个联系人了。

  上面代码只是保存了一个还原点,即备忘录中只保存了3个联系人的数据,但是,如果想备份多个还原点怎么办呢?即恢复到3个人后,又想恢复到前面2个人的状态,这时候可能你会想,这样没必要啊,到时候在删除不就好了。但是如果在实际应用中,可能我们发了很多时间去创建通讯录中只有2个联系人的状态,恢复到3个人的状态后,发现这个状态时错误的,还是原来2个人的状态是正确的,难道我们又去花之前的那么多时间去重复操作吗?这显然不合理,如果就思考,能不能保存多个还原点呢?保存多个还原点其实很简单,只需要保存多个备忘录对象就可以了。具体实现代码如下所示:

  1 namespace MultipleMementoPattern
  2 {
  3     // 联系人
  4     public class ContactPerson
  5     {
  6         public string Name { get; set; }
  7         public string MobileNum { get; set; }
  8     }
  9
 10     // 发起人
 11     public class MobileOwner
 12     {
 13         public List<ContactPerson> ContactPersons { get; set; }
 14         public MobileOwner(List<ContactPerson> persons)
 15         {
 16             ContactPersons = persons;
 17         }
 18
 19         // 创建备忘录,将当期要保存的联系人列表导入到备忘录中
 20         public ContactMemento CreateMemento()
 21         {
 22             return new ContactMemento(new List<ContactPerson>(this.ContactPersons));
 23         }
 24
 25         // 将备忘录中的数据备份导入到联系人列表中
 26         public void RestoreMemento(ContactMemento memento)
 27         {
 28             if (memento != null)
 29             {
 30                 this.ContactPersons = memento.ContactPersonBack;
 31             }
 32         }
 33         public void Show()
 34         {
 35             Console.WriteLine("联系人列表中有{0}个人,他们是:", ContactPersons.Count);
 36             foreach (ContactPerson p in ContactPersons)
 37             {
 38                 Console.WriteLine("姓名: {0} 号码为: {1}", p.Name, p.MobileNum);
 39             }
 40         }
 41     }
 42
 43     // 备忘录
 44     public class ContactMemento
 45     {
 46         public List<ContactPerson> ContactPersonBack {get;set;}
 47         public ContactMemento(List<ContactPerson> persons)
 48         {
 49             ContactPersonBack = persons;
 50         }
 51     }
 52
 53     // 管理角色
 54     public class Caretaker
 55     {
 56         // 使用多个备忘录来存储多个备份点
 57         public Dictionary<string, ContactMemento> ContactMementoDic { get; set; }
 58         public Caretaker()
 59         {
 60             ContactMementoDic = new Dictionary<string, ContactMemento>();
 61         }
 62     }
 63
 64     class Program
 65     {
 66         static void Main(string[] args)
 67         {
 68             List<ContactPerson> persons = new List<ContactPerson>()
 69             {
 70                 new ContactPerson() { Name= "Learning Hard", MobileNum = "123445"},
 71                 new ContactPerson() { Name = "Tony", MobileNum = "234565"},
 72                 new ContactPerson() { Name = "Jock", MobileNum = "231455"}
 73             };
 74
 75             MobileOwner mobileOwner = new MobileOwner(persons);
 76             mobileOwner.Show();
 77
 78             // 创建备忘录并保存备忘录对象
 79             Caretaker caretaker = new Caretaker();
 80             caretaker.ContactMementoDic.Add(DateTime.Now.ToString(), mobileOwner.CreateMemento());
 81
 82             // 更改发起人联系人列表
 83             Console.WriteLine("----移除最后一个联系人--------");
 84             mobileOwner.ContactPersons.RemoveAt(2);
 85             mobileOwner.Show();
 86
 87             // 创建第二个备份
 88             Thread.Sleep(1000);
 89             caretaker.ContactMementoDic.Add(DateTime.Now.ToString(), mobileOwner.CreateMemento());
 90
 91             // 恢复到原始状态
 92             Console.WriteLine("-------恢复联系人列表,请从以下列表选择恢复的日期------");
 93             var keyCollection = caretaker.ContactMementoDic.Keys;
 94             foreach (string k in keyCollection)
 95             {
 96                 Console.WriteLine("Key = {0}", k);
 97             }
 98             while (true)
 99             {
100                 Console.Write("请输入数字,按窗口的关闭键退出:");
101
102                 int index = -1;
103                 try
104                 {
105                     index = Int32.Parse(Console.ReadLine());
106                 }
107                 catch
108                 {
109                     Console.WriteLine("输入的格式错误");
110                     continue;
111                 }
112
113                 ContactMemento contactMentor = null;
114                 if (index < keyCollection.Count && caretaker.ContactMementoDic.TryGetValue(keyCollection.ElementAt(index), out contactMentor))
115                 {
116                     mobileOwner.RestoreMemento(contactMentor);
117                     mobileOwner.Show();
118                 }
119                 else
120                 {
121                     Console.WriteLine("输入的索引大于集合长度!");
122                 }
123             }
124         }
125     }
126 }

  这样就保存了多个状态,客户端可以选择恢复的状态点,具体运行结果如下所示:

三、备忘录模式的适用场景

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

  • 如果系统需要提供回滚操作时,使用备忘录模式非常合适。例如文本编辑器的Ctrl+Z撤销操作的实现,数据库中事务操作。

四、备忘录模式的优缺点

  备忘录模式具有以下优点:

  • 如果某个操作错误地破坏了数据的完整性,此时可以使用备忘录模式将数据恢复成原来正确的数据。
  • 备份的状态数据保存在发起人角色之外,这样发起人就不需要对各个备份的状态进行管理。而是由备忘录角色进行管理,而备忘录角色又是由管理者角色管理,符合单一职责原则。

  当然,备忘录模式也存在一定的缺点:

  • 在实际的系统中,可能需要维护多个备份,需要额外的资源,这样对资源的消耗比较严重。

五、总结

  备忘录模式主要思想是——利用备忘录对象来对保存发起人的内部状态,当发起人需要恢复原来状态时,再从备忘录对象中进行获取,在实际开发过程也应用到这点,例如数据库中的事务处理。

时间: 2024-08-07 00:15:18

C#设计模式(23)——备忘录模式(Memento Pattern)的相关文章

23.备忘录模式(Memento Pattern)

using System; using System.Collections.Generic; namespace ConsoleApplication6 { /// <summary> /// 从字面意思就可以明白,备忘录模式就是对某个类的状态进行保存下来, /// 等到需要恢复的时候, /// 可以从备忘录中进行恢复.生活中这样的例子经常看到, /// 如备忘电话通讯录,备份操作操作系统,备份数据库等. /// </summary> class Program { static

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

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 阅读前一篇<设计模式(行为型)之中介者模式(Mediator Pattern)>http://blog.csdn.net/yanbober/article/details/45533335 概述 备忘录模式提供了一种状态恢复的实现机制,使得用户可以方便地回到一个特定的历史步骤,当新的状态无

设计模式之备忘录模式(Memento)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以

备忘录模式(Memento Pattern)

备忘录模式(Memento Pattern)用来恢复一个对象的状态到以前的状态. 备忘录模式下属于行为模式的类别. 实现实例 在这个示例中,备忘录模式(Memento Pattern)使用三个操作者类. Memento类中包含要恢复的对象的状态. 创建者在Memento对象中创建和存储状态,Caretaker对象负责从Memento中恢复对象状态.在这个示例中,创建了以下几个类:Memento,Originator和CareTaker. MementoPatternDemo这是一个演示类,它将使

[设计模式] 备忘录模式Memento Pattern

在GOF的<设计模式:可复用面向对象软件的基础>一书中对备忘录模式是这样说的:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态. 类图和实例: 简单的模式实例: #include <iostream> #include <string> using namespace std; class Memento { private:     string state; public:     Memento(

【设计模式】—— 备忘录模式Memento

前言:[模式总览]——————————by xingoo 模式意图 这个模式主要是想通过一个对象来记录对象的某种状态,这样有利于在其他需要的场合进行恢复. 该模式还有跟多可以扩展的地方,比如可以记录多个时间的状态,每个角色都有可以扩展的空间,完全看业务场景而定. 应用场景 1 保存对象某一时刻的状态 2 避免直接暴露接口,破坏封装性 模式结构 Originator 是备忘录的发起者,记录状态的对象 class Originator{ private String state; public Me

用最简单的例子理解备忘录模式(Memento Pattern)

简单来说,备忘录模式就是支持回退操作.假设让一个Notepad支持回退操作,如何实现呢? 首先需要一个备忘录类. public class Memento { private string _msg; public Memento(string msg) { _msg = msg; } public string GetText() { return _msg; } } 以上,○ 构造函数在Nodepad每次记录信息的时候调用,Nodepad所记录的信息最终通过该构造函数赋值给了_msg字段.○

C#面向对象设计之——备忘录模式Memento Pattern(二十三)

一.前言 备忘录模式:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,这样以后就可将该对象回复到原先保存的状态. 二.结构图 三.实例代码 using System; using System.Collections.Generic; using System.Text; namespace 备忘录模式 { class Program { static void Main(string[] args) { Originator o = new Originator()

设计模式 笔记 备忘录模式 Memento

//---------------------------15/04/27---------------------------- //Memento 备忘录模式----对象行为型模式 /* 1:意图: 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可讲该对象 恢复到原先保存的状态. 2:别名: Token 3:动机: 4:适用性: 1>必须保存一个对象在某一个时刻的状态,这样以后需要时它才能恢复到先前的状态. 2>如果一个用接口来让其它对象直接得到这些