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 = "123445"},new ContactPerson() { Name = "Tony", MobileNum = "234565"},new ContactPerson() { Name = "Jock", MobileNum = "231455"}};MobileOwner mobileOwner = new MobileOwner(persons);mobileOwner.Show();// 创建备忘录并保存备忘录对象Caretaker caretaker = new Caretaker();caretaker.ContactM = mobileOwner.CreateMemento();// 更改发起人联系人列表Console.WriteLine("----移除最后一个联系人--------");mobileOwner.ContactPersons.RemoveAt(2);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 = "123445"},new ContactPerson() { Name = "Tony", MobileNum = "234565"},new ContactPerson() { Name = "Jock", MobileNum = "231455"}};MobileOwner mobileOwner = new MobileOwner(persons);mobileOwner.Show();// 创建备忘录并保存备忘录对象Caretaker caretaker = new Caretaker();caretaker.ContactMementoDic.Add(DateTime.Now.ToString(), mobileOwner.CreateMemento());// 更改发起人联系人列表Console.WriteLine("----移除最后一个联系人--------");mobileOwner.ContactPersons.RemoveAt(2);mobileOwner.Show();// 创建第二个备份Thread.Sleep(1000);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 = -1;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设计模式之目录 

 

转载于:https://www.cnblogs.com/xcsn/p/7498695.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/415923.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mpvue 从零开始 女友初成长 0

我的女友叫mpvue&#xff0c;为什么不选择原生的&#xff0c;或者wepy呢&#xff0c;因为我只喜欢mpvue。 0、首先你得保证先安装了vue-cli npm install --g vue-cli1、脚手架构建项目&#xff0c;我直接在当前项目中创建&#xff0c;一路Y就可以创建项目了。 vue init mpvu…

Android 隐藏状态栏,沉浸式状态栏,状态栏背景色,状态栏字体色,透明状态工具类

设置状态栏颜色 if (Build.VERSION.SDK_INT>21){getWindow().setStatusBarColor(getResources().getColor(R.color.mainc)); } 方法2 <color name"colorPrimary">#3F51B5</color> //取消标题 requestWindowFeature(Window.FEATURE_NO_TITLE); /…

SVN错误信息汇总

Subversion 错误信息一览表 注意&#xff1a; 不同的客户端&#xff08;命令行&#xff0c;TortoiseSVN, AnkhSVN, Subclipse等&#xff09;的出错信息可能稍有不同。 下面表格中的出错信息以 http://svn.moon.ossxp.com/svn/test 版本库做示例&#xff0c;仅供参考。 编…

mpvue 从零开始 女友的衣装 1 pages

pages文件夹就像一个大橱柜&#xff0c;里面放着各种精美的衣装&#xff0c;你也可以理解为供小程序的页面。 1、制造衣服 我在pages页面下新建了3个页面 market 广告市场task 任务中心my 个人中心 以market为例&#xff0c;写最简单的代码 <template><div class…

Oracle RDA(Remote Diagnostic Agent) 工具说明

Oracle RDA(Remote Diagnostic Agent) 工具说明 分类&#xff1a; Oracle 性能调优 Oracle 高级知识 一.RDA 说明 RDA(RemoteDiagnostic Agent)是oracle用来收集、分析数据库的工具&#xff0c;运行该工具不会改变系统的任何参数&#xff0c;RDA收集的相关数据非常全面&…

mpvue 从零开始 女友的发带 2 window中设置

女友头上发带的颜色和文字是可以设置的&#xff0c;通过配置app.json中的window参数。 "window": {"backgroundTextStyle": "light","navigationBarBackgroundColor": "#58A94E","navigationBarTitleText": "…

mysql中独立表空间与共享表空间之前如何切换

环境 mysql版本&#xff1a;5.7.19 官方文档&#xff1a;(https://dev.mysql.com/doc/refman/5.7/en/innodb-multiple-tablespaces.html) 查看目前属于哪种表空间 mysql> show variables like %per_%; 共享表空间会显示为off&#xff1a; 独立表空间会显示为on&#xff1a; …

android 标题栏颜色渐变和阴影,ScrollView上下滑动监听,及判断scrollView是否滚动到底部

1、创建 ScrollListener 接口监听滑动距离 public interface ScrollListener {void onScrollChanged(ScrollListenerView scrollView, int x, int y, int oldX, int oldY); }view阴影属性 android:elevation"4dp"2、重写 ScrollView 自定义 ScrollListenerView …

Linux下MySQL 安装配置

MySQL 是最流行的关系型数据库管理系统&#xff0c;由瑞典MySQL AB公司开发&#xff0c;目前属于Oracle公司。 MySQL所使用的SQL语言是用于访问数据库的最常用标准化语言。 MySQL由于其体积小、速度快、总体拥有成本低&#xff0c;尤其是开放源码这一特点&#xff0c;一般中小型…

android 监听webView滑动距离和标题栏颜色渐变

重写webView之 X5WebView import android.annotation.SuppressLint; import android.content.Context; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Log; import android.view.ActionMode; import android.view.Menu; …

mpvue 从零开始 女友拉黑了我 5 不在以下request 合法域名列表中,请参考文档

上一篇&#xff0c;才调通了接口&#xff0c;试了几次&#xff0c;都成功&#xff0c;突然&#xff0c;微信报错了。 VM6239:1 https://www.easy-mock.com 不在以下 request 合法域名列表中&#xff0c;请参考文档&#xff1a;https://mp.weixin.qq.com/debug/wxadoc/dev/api…

Javascript与C#中使用正则表达式

JavaScript RegExp 对象 新建一个RegExp对象 new RegExp(pattern,[attributes]) 注&#xff1a; \d需要使用[0-9]来代替 参数 参数 pattern 是一个字符串&#xff0c;指定了正则表达式的模式或其他正则表达式。 参数 attributes 是一个可选的字符串&#xff0c;包含…