Head First设计模式之备忘录模式
一、定义
不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样就可以将该对象恢复到原先保存的状态
二、结构

备忘录模式中主要有三类角色:
- 发起人角色:记录当前时刻的内部状态,负责创建和恢复备忘录数据。
- 备忘录角色:负责存储发起人对象的内部状态,在进行恢复时提供给发起人需要的状态。
- 管理者角色:负责保存备忘录对象,但是不能对备忘录对象的内容进行操作或检查。
三、适用场景
1、需要保存/恢复数据的相关状态场景。
2、提供一个可回滚的操作。
四、优缺点
优点:
1、给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。
2、实现了信息的封装,使得用户不需要关心状态的保存细节。
缺点:
消耗资源。
1、如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
2、由于备份的信息是由发起人自己提供的,所以管理者无法预知备份的信息的大小,存在一定的未知风险。
五、实现
下面以备份手机通讯录为例子来实现了备忘录模式,具体的实现代码如下所示:
单次备份
// 联系人
public class ContactPerson
{
public string Name { get; set; }
public string MobileNum { get; set; }
} // 发起人
public class MobileOwner
{
// 发起人需要保存的内部状态
public List<ContactPerson> ContactPersons { get; set; } public MobileOwner(List<ContactPerson> persons)
{
ContactPersons = persons;
} // 创建备忘录,将当期要保存的联系人列表导入到备忘录中
public ContactMemento CreateMemento()
{
// 这里也应该传递深拷贝,new List方式传递的是浅拷贝,
// 因为ContactPerson类中都是string类型,所以这里new list方式对ContactPerson对象执行了深拷贝
// 如果ContactPerson包括非string的引用类型就会有问题,所以这里也应该用序列化传递深拷贝
return new ContactMemento(new List<ContactPerson>(this.ContactPersons));
} // 将备忘录中的数据备份导入到联系人列表中
public void RestoreMemento(ContactMemento memento)
{
// 下面这种方式是错误的,因为这样传递的是引用,
// 则删除一次可以恢复,但恢复之后再删除的话就恢复不了.
// 所以应该传递contactPersonBack的深拷贝,深拷贝可以使用序列化来完成
this.ContactPersons = memento.contactPersonBack;
} public void Show()
{
Console.WriteLine("联系人列表中有{0}个人,他们是:", ContactPersons.Count);
foreach (ContactPerson p in ContactPersons)
{
Console.WriteLine("姓名: {0} 号码为: {1}", p.Name, p.MobileNum);
}
}
} // 备忘录
public class ContactMemento
{
// 保存发起人的内部状态
public List<ContactPerson> contactPersonBack; public ContactMemento(List<ContactPerson> persons)
{
contactPersonBack = persons;
}
} // 管理角色
public class Caretaker
{
public ContactMemento ContactM { get; set; }
} class Program
{
static void Main(string[] args)
{
List<ContactPerson> persons = new List<ContactPerson>()
{
new ContactPerson() { Name= "Learning Hard", MobileNum = ""},
new ContactPerson() { Name = "Tony", MobileNum = ""},
new ContactPerson() { Name = "Jock", MobileNum = ""}
};
MobileOwner mobileOwner = new MobileOwner(persons);
mobileOwner.Show(); // 创建备忘录并保存备忘录对象
Caretaker caretaker = new Caretaker();
caretaker.ContactM = mobileOwner.CreateMemento(); // 更改发起人联系人列表
Console.WriteLine("----移除最后一个联系人--------");
mobileOwner.ContactPersons.RemoveAt();
mobileOwner.Show(); // 恢复到原始状态
Console.WriteLine("-------恢复联系人列表------");
mobileOwner.RestoreMemento(caretaker.ContactM);
mobileOwner.Show(); Console.Read();
}
}
多次备份
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace DesignPatterns.Mememto
{
// 联系人
public class ContactPerson
{
public string Name { get; set; }
public string MobileNum { get; set; }
} // 发起人
public class MobileOwner
{
public List<ContactPerson> ContactPersons { get; set; }
public MobileOwner(List<ContactPerson> persons)
{
ContactPersons = persons;
} // 创建备忘录,将当期要保存的联系人列表导入到备忘录中
public ContactMemento CreateMemento()
{
// 这里也应该传递深拷贝,new List方式传递的是浅拷贝,
// 因为ContactPerson类中都是string类型,所以这里new list方式对ContactPerson对象执行了深拷贝
// 如果ContactPerson包括非string的引用类型就会有问题,所以这里也应该用序列化传递深拷贝
return new ContactMemento(new List<ContactPerson>(this.ContactPersons));
} // 将备忘录中的数据备份导入到联系人列表中
public void RestoreMemento(ContactMemento memento)
{
if (memento != null)
{
// 下面这种方式是错误的,因为这样传递的是引用,
// 则删除一次可以恢复,但恢复之后再删除的话就恢复不了.
// 所以应该传递contactPersonBack的深拷贝,深拷贝可以使用序列化来完成
this.ContactPersons = memento.ContactPersonBack;
}
}
public void Show()
{
Console.WriteLine("联系人列表中有{0}个人,他们是:", ContactPersons.Count);
foreach (ContactPerson p in ContactPersons)
{
Console.WriteLine("姓名: {0} 号码为: {1}", p.Name, p.MobileNum);
}
}
} // 备忘录
public class ContactMemento
{
public List<ContactPerson> ContactPersonBack { get; set; }
public ContactMemento(List<ContactPerson> persons)
{
ContactPersonBack = persons;
}
} // 管理角色
public class Caretaker
{
// 使用多个备忘录来存储多个备份点
public Dictionary<string, ContactMemento> ContactMementoDic { get; set; }
public Caretaker()
{
ContactMementoDic = new Dictionary<string, ContactMemento>();
}
} class Program
{
static void Main(string[] args)
{
List<ContactPerson> persons = new List<ContactPerson>()
{
new ContactPerson() { Name= "Learning Hard", MobileNum = ""},
new ContactPerson() { Name = "Tony", MobileNum = ""},
new ContactPerson() { Name = "Jock", MobileNum = ""}
}; MobileOwner mobileOwner = new MobileOwner(persons);
mobileOwner.Show(); // 创建备忘录并保存备忘录对象
Caretaker caretaker = new Caretaker();
caretaker.ContactMementoDic.Add(DateTime.Now.ToString(), mobileOwner.CreateMemento()); // 更改发起人联系人列表
Console.WriteLine("----移除最后一个联系人--------");
mobileOwner.ContactPersons.RemoveAt();
mobileOwner.Show(); // 创建第二个备份
Thread.Sleep();
caretaker.ContactMementoDic.Add(DateTime.Now.ToString(), mobileOwner.CreateMemento()); // 恢复到原始状态
Console.WriteLine("-------恢复联系人列表,请从以下列表选择恢复的日期------");
var keyCollection = caretaker.ContactMementoDic.Keys;
foreach (string k in keyCollection)
{
Console.WriteLine("Key = {0}", k);
}
while (true)
{
Console.Write("请输入数字,按窗口的关闭键退出:"); int index = -;
try
{
index = Int32.Parse(Console.ReadLine());
}
catch
{
Console.WriteLine("输入的格式错误");
continue;
} ContactMemento contactMentor = null;
if (index < keyCollection.Count && caretaker.ContactMementoDic.TryGetValue(keyCollection.ElementAt(index), out contactMentor))
{
mobileOwner.RestoreMemento(contactMentor);
mobileOwner.Show();
}
else
{
Console.WriteLine("输入的索引大于集合长度!");
}
}
}
}
}
参考
http://www.cnblogs.com/JsonShare/p/7283972.html
http://www.runoob.com/design-pattern/memento-pattern.html
http://www.cnblogs.com/zhili/p/MementoPattern.html
欢迎阅读本系列文章:Head First设计模式之目录
Head First设计模式之备忘录模式的更多相关文章
- 乐在其中设计模式(C#) - 备忘录模式(Memento Pattern)
原文:乐在其中设计模式(C#) - 备忘录模式(Memento Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 备忘录模式(Memento Pattern) 作者:webabc ...
- 折腾Java设计模式之备忘录模式
原文地址:折腾Java设计模式之备忘录模式 备忘录模式 Without violating encapsulation, capture and externalize an object's int ...
- C#设计模式:备忘录模式(Memento Pattern)
一,C#设计模式:备忘录模式(Memento Pattern) 1.发起人角色(Originator):记录当前时刻的内部状态,负责创建和恢复备忘录数据.负责创建一个备忘录Memento,用以记录当前 ...
- js设计模式——7.备忘录模式
js设计模式——7.备忘录模式 /*js设计模式——备忘录模式*/ // 备忘类 class Memento { constructor(content) { this.content = conte ...
- java设计模式之备忘录模式
备忘录模式 备忘录模式是一种软件设计模式:在不破坏封闭的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存的状态.一听到备忘录这个字的时候想起了小小时打的游 ...
- 【GOF23设计模式】备忘录模式
来源:http://www.bjsxt.com/ 一.[GOF23设计模式]_备忘录模式.多点备忘.事务操作.回滚数据底层架构 package com.test.memento; /** * 源发器类 ...
- [设计模式] 18 备忘录模式Memento Pattern
在GOF的<设计模式:可复用面向对象软件的基础>一书中对备忘录模式是这样说的:在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态.这样以后就可将该对象恢复到原先保存 ...
- 再起航,我的学习笔记之JavaScript设计模式24(备忘录模式)
备忘录模式 概念介绍 备忘录模式(Memento): 在不破坏对象的封装性的前提下,在对象之外捕获并保存该对象内部的状态以便日后对象使用或者对象恢复到以前的某个状态. 简易分页 在一般情况下我们需要做 ...
- 【Unity与23种设计模式】备忘录模式(Memento)
GoF中定义: "在不违反封装的原则下,获取一个对象的内部状态并保留在外部,让对象可以在日后恢复到原先保留时的状态." 对于一些需要存储的数据,比如历史最高分 当与得分减分系统写入 ...
随机推荐
- PCA, SVD以及代码示例
本文是对PCA和SVD学习的整理笔记,为了避免很多重复内容的工作,我会在介绍概念的时候引用其他童鞋的工作和内容,具体来源我会标记在参考资料中. 一.PCA (Principle component a ...
- Redis的简单使用和介绍
1.什么是NoSQL NoSQL = Not Only SQL 非关系型的数据库 2. 为什么需要NoSQL High performance 高并发读写 ...
- JPA学习笔记(8)——映射双向一对多关联关系
双向一对多关联关系 前面的博客讲的都是单向的,而本问讲的是双向的(双向一对多 = 双向多对一) 什么是双向? 我们来对照一下单向和双向 单向/双向 User实体类中是否有List< Order& ...
- hdu 4409 Family Name List(LCA&有坑点)
Family Name List Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) ...
- 在Intellij里使用Erlang依赖库
这里以protobuffs为例,记录一下环境的配置,发现这种东西中文的资料真的不多,无论是分享还是记录都是很好的 1.创建一个文件夹名use_proto, 配置rebar.config文件如下: {d ...
- Ajax顺序执行
循环中的Ajax 在前后端分离的项目中,Ajax是连接前后端的枢纽. 需求:有一个需要循环发起n次的请求,但是n次循环传参不同,我并不知道n是多少,并且要求能够保证返回顺序.JSONP用同步锁无效 示 ...
- centos6.5安装禅道
1.安装禅道需要安装以下环境 mysql php 5 apache 2 2.安装命令 1.安装mysql yum install mysql mysql-server 2.安装apache yum i ...
- Git使用简单总结
创建版本库git add加入到暂存区git commit -m" "加入到分支 时光机穿梭git satus查看仓库的当前状态git diff file 查看修改内容 版本回退HE ...
- 日期函数ADD_MONTHS,MONTHS_BETWEEN,LAST_DAY,NEXT_DAY
- JMeter获取JSON内容
source("D:\\apache-jmeter-3.0\\用例\\Test.java"); public static void f(){ String response_da ...